<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Softra Rent API client with JWT token caching.
 * Uses native cURL (matching softra-test.php) instead of WP HTTP API.
 */
class Carei_Softra_API {

    private static $instance = null;

    private $base_url;
    private $username;
    private $password;

    const TOKEN_TRANSIENT = 'carei_softra_token';
    const TOKEN_EXPIRY    = 3000; // 50 minutes (token valid 60 min)

    private function __construct( $base_url, $username, $password ) {
        $this->base_url = rtrim( $base_url, '/' );
        $this->username = $username;
        $this->password = $password;
    }

    public static function init( $base_url, $username, $password ) {
        if ( null === self::$instance ) {
            self::$instance = new self( $base_url, $username, $password );
        }
        return self::$instance;
    }

    public static function get_instance() {
        return self::$instance;
    }

    /**
     * Native cURL request — mirrors softra-test.php requestJson().
     */
    private function curl_request( $method, $url, $payload = null, $extra_headers = array() ) {
        $ch = curl_init( $url );
        if ( false === $ch ) {
            return new WP_Error( 'carei_curl_init', 'cURL init failed' );
        }

        $headers = array_merge( array( 'Accept: application/json' ), $extra_headers );
        if ( null !== $payload ) {
            $headers[] = 'Content-Type: application/json';
        }

        curl_setopt_array( $ch, array(
            CURLOPT_CUSTOMREQUEST  => strtoupper( $method ),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => $headers,
            CURLOPT_TIMEOUT        => 30,
            CURLOPT_CONNECTTIMEOUT => 15,
        ) );

        if ( null !== $payload ) {
            curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) );
        }

        $raw    = curl_exec( $ch );
        $err    = curl_error( $ch );
        $status = (int) curl_getinfo( $ch, CURLINFO_HTTP_CODE );
        curl_close( $ch );

        if ( false === $raw ) {
            return new WP_Error( 'carei_curl_error', 'cURL error: ' . $err );
        }

        $decoded = json_decode( $raw, true );
        if ( ! is_array( $decoded ) ) {
            $decoded = null;
        }

        return array(
            'status' => $status,
            'body'   => $decoded,
            'raw'    => $raw,
        );
    }

    /**
     * Get JWT token (cached in WP transient).
     */
    public function get_token() {
        $token = get_transient( self::TOKEN_TRANSIENT );
        if ( false !== $token ) {
            return $token;
        }

        $res = $this->curl_request( 'POST', $this->base_url . '/account/auth', array(
            'login'    => $this->username,
            'password' => $this->password,
        ) );

        if ( is_wp_error( $res ) ) {
            return new WP_Error( 'carei_auth_failed', 'Softra API auth failed: ' . $res->get_error_message() );
        }

        if ( 200 !== $res['status'] || empty( $res['body']['token'] ) ) {
            $detail = isset( $res['raw'] ) ? $res['raw'] : '';
            return new WP_Error( 'carei_auth_failed', 'Softra API auth failed (HTTP ' . $res['status'] . '): ' . $detail );
        }

        $token = $res['body']['token'];
        set_transient( self::TOKEN_TRANSIENT, $token, self::TOKEN_EXPIRY );

        return $token;
    }

    /**
     * Generic API request with auth.
     */
    public function request( $method, $endpoint, $body = null, $query_params = array() ) {
        $token = $this->get_token();
        if ( is_wp_error( $token ) ) {
            return $token;
        }

        $url = $this->base_url . $endpoint;
        if ( ! empty( $query_params ) ) {
            $url .= '?' . http_build_query( $query_params );
        }

        $res = $this->curl_request(
            $method,
            $url,
            $body,
            array( 'Authorization: Bearer ' . $token )
        );

        if ( is_wp_error( $res ) ) {
            return $res;
        }

        if ( $res['status'] < 200 || $res['status'] >= 300 ) {
            return new WP_Error(
                'carei_api_error',
                'Softra API error: HTTP ' . $res['status'],
                array( 'status' => $res['status'], 'body' => $res['body'] )
            );
        }

        return $res['body'];
    }

    // ─── Public API Methods ───────────────────────────────────────

    public function get_branches() {
        return $this->request( 'GET', '/branch/list' );
    }

    public function get_all_car_classes() {
        return $this->request( 'GET', '/car/class/listAll' );
    }

    /**
     * Get branches list with all segment names.
     * Returns branches + all segments. Frontend shows all branches for any segment
     * (actual availability verified at booking time by the API).
     * Cached as WP transient for 6 hours.
     */
    public function get_segments_branches_map() {
        $cache_key = 'carei_segments_branches_map';
        $cached    = get_transient( $cache_key );
        if ( false !== $cached ) {
            return $cached;
        }

        $branches = $this->get_branches();
        if ( is_wp_error( $branches ) || ! is_array( $branches ) ) {
            return is_wp_error( $branches ) ? $branches : array();
        }

        $all_classes = $this->get_all_car_classes();
        $segments    = array();
        if ( ! is_wp_error( $all_classes ) && is_array( $all_classes ) ) {
            foreach ( $all_classes as $cls ) {
                $seg = is_string( $cls ) ? $cls : ( isset( $cls['name'] ) ? $cls['name'] : '' );
                if ( '' !== $seg ) {
                    $segments[] = $seg;
                }
            }
        }

        // All branches potentially have all segments.
        // Actual availability is checked at booking via /car/class/list.
        $branch_names = array();
        foreach ( $branches as $b ) {
            if ( ! empty( $b['name'] ) ) {
                $branch_names[] = $b['name'];
            }
        }

        $map = array();
        foreach ( $segments as $seg ) {
            $map[ $seg ] = $branch_names;
        }

        $result = array(
            'segmentToBranches' => $map,
            'branchToSegments'  => array(),
            'branches'          => $branches,
        );

        set_transient( $cache_key, $result, 6 * HOUR_IN_SECONDS );

        return $result;
    }

    public function get_car_classes( $date_from, $date_to, $branch_name ) {
        return $this->request( 'POST', '/car/class/list', array(
            'dateFrom'   => $date_from,
            'dateTo'     => $date_to,
            'branchName' => $branch_name,
        ) );
    }

    public function get_car_models( $date_from, $date_to, $branch_name, $category = '' ) {
        return $this->request( 'POST', '/car/model/list', array(
            'dateFrom'   => $date_from,
            'dateTo'     => $date_to,
            'branchName' => $branch_name,
            'category'   => $category,
        ), array( 'includeBrandDetails' => 'true' ) );
    }

    public function get_pricelist( $category, $date_from, $date_to, $pickup_location, $language = 'pl', $currency = 'PLN' ) {
        return $this->request( 'POST', '/pricelist/list', array(
            'category'       => $category,
            'dateFrom'       => $date_from,
            'dateTo'         => $date_to,
            'pickUpLocation' => $pickup_location,
            'language'       => $language,
            'currency'       => $currency,
        ) );
    }

    public function get_pricing_summary( $params ) {
        return $this->request( 'POST', '/rent/princingSummary', $params );
    }

    public function add_customer( $data ) {
        return $this->request( 'POST', '/customer/add', $data );
    }

    public function make_booking( $data ) {
        return $this->request( 'POST', '/rent/makebooking', $data );
    }

    public function confirm_booking( $reservation_id ) {
        return $this->request( 'POST', '/rent/confirm', array(
            'reservationId' => $reservation_id,
        ) );
    }

    public function cancel_booking( $reservation_id, $reason ) {
        return $this->request( 'POST', '/rent/cancel', array(
            'reservationId' => $reservation_id,
            'reason'        => $reason,
        ) );
    }

    public function get_agreements() {
        return $this->request( 'GET', '/agreement/def/list' );
    }
}
