<?php

if (!defined('ABSPATH')) { exit; }

class AIEO_MOI_Provider_Etsy implements AIEO_MOI_Provider {

    const API_BASE  = 'https://openapi.etsy.com/v3/application';
    const TOKEN_URL = 'https://api.etsy.com/v3/public/oauth/token';

    public function slug()  { return 'etsy'; }
    public function label() { return 'Etsy'; }

    public function info_text() {
        return '<strong>' . esc_html__('Etsy Open API v3', 'ai-eshop-optimizer') . '</strong><br>'
             . esc_html__('Auth: OAuth2 (PKCE). Run the one-time Etsy app authorisation, then paste the keystring (API key), the resulting refresh token, and your numeric shop id below.', 'ai-eshop-optimizer') . '<br>'
             . esc_html__('Delivery: Poll only (no order webhooks). Orders arrive already paid; there is no accept/reject — cancellations + refunds happen in the Etsy dashboard and are synced back on the next poll.', 'ai-eshop-optimizer');
    }

    public function credential_fields() {
        return array(
            array(
                'name'        => 'keystring',
                'label'       => __('Etsy API key (keystring)', 'ai-eshop-optimizer'),
                'type'        => 'text',
                'required'    => true,
                'description' => __('Your Etsy app’s keystring — sent as the x-api-key header.', 'ai-eshop-optimizer'),
            ),
            array(
                'name'        => 'refresh_token',
                'label'       => __('OAuth refresh token', 'ai-eshop-optimizer'),
                'type'        => 'password',
                'required'    => true,
                'description' => __('From the one-time OAuth connect (scopes: transactions_r, address_r, email_r). Auto-rotates after first use.', 'ai-eshop-optimizer'),
            ),
            array(
                'name'        => 'shop_id',
                'label'       => __('Etsy shop id', 'ai-eshop-optimizer'),
                'type'        => 'text',
                'required'    => true,
                'description' => __('Numeric shop id (getMeShop / your shop URL). All receipts are read under this shop.', 'ai-eshop-optimizer'),
            ),
        );
    }

    public function validate_credentials(array $credentials) {
        return !empty($credentials['keystring'])
            && !empty($credentials['refresh_token'])
            && !empty($credentials['shop_id']);
    }

    public function verify_webhook($request, array $vendor) {
        return new WP_Error('no_webhooks', __('Etsy is poll-only — no webhook delivery.', 'ai-eshop-optimizer'));
    }

    public function parse_webhook_payload(array $body) {
        return array();
    }

    public function accept_order(array $vendor, $remote_order_id, array $opts = array()) {
        
        return array('ok' => true, 'message' => __('Etsy orders are pre-paid — no acceptance needed.', 'ai-eshop-optimizer'));
    }

    public function reject_order(array $vendor, $remote_order_id, $reason = '') {
        return array('ok' => false, 'message' => __('Etsy has no order-rejection API — cancel or refund the order in the Etsy dashboard; it syncs back on the next poll.', 'ai-eshop-optimizer'));
    }

    public function fetch_voucher(array $vendor, $remote_order_id) {
        return ''; 
    }

    public function poll_orders(array $vendor, $since_ts) {
        $res = $this->fetch_window($vendor, (int) $since_ts, 0);
        return is_wp_error($res) ? array() : $res; 
    }

    public function backfill_orders(array $vendor, $since_ts, $until_ts) {
        $res = $this->fetch_window($vendor, (int) $since_ts, (int) $until_ts);
        if (is_wp_error($res)) {
            
            throw new \RuntimeException('Etsy API: ' . $res->get_error_message());
        }
        return $res;
    }

    

    
    private function fetch_window(array $vendor, $since_ts, $until_ts) {
        $shop_id = (int) ($vendor['credentials']['shop_id'] ?? 0);
        if ($shop_id <= 0) {
            return new WP_Error('etsy_no_shop', __('Etsy shop id is missing.', 'ai-eshop-optimizer'));
        }
        $min_lm  = max(0, (int) $since_ts - 120); 
        $limit   = 100;
        $offset  = 0;
        $guard   = 0;
        $out     = array();

        while ($guard++ < 50) {
            $query = array(
                'min_last_modified' => $min_lm,
                'was_paid'          => 'true',
                'limit'             => $limit,
                'offset'            => $offset,
                'sort_on'           => 'updated',
                'sort_order'        => 'asc',
            );
            if ((int) $until_ts > 0) { $query['max_last_modified'] = (int) $until_ts; }

            $resp = $this->api_get($vendor, '/shops/' . $shop_id . '/receipts', $query);
            if (is_wp_error($resp)) {
                if ($offset === 0) { return $resp; } 
                break;
            }
            $results = isset($resp['results']) && is_array($resp['results']) ? $resp['results'] : array();
            if (empty($results)) { break; }

            foreach ($results as $receipt) {
                if (is_array($receipt)) { $out[] = $this->normalise_receipt($receipt); }
            }
            if (count($results) < $limit) { break; }
            $offset += $limit;
        }
        return $out;
    }

    
    private function normalise_receipt(array $r) {
        list($first, $last) = $this->split_name((string) ($r['name'] ?? ''));
        $address = array(
            'first_name' => $first,
            'last_name'  => $last,
            'company'    => '',
            'address_1'  => (string) ($r['first_line'] ?? ''),
            'address_2'  => (string) ($r['second_line'] ?? ''),
            'city'       => (string) ($r['city'] ?? ''),
            'state'      => (string) ($r['state'] ?? ''),
            'postcode'   => (string) ($r['zip'] ?? ''),
            'country'    => (string) ($r['country_iso'] ?? ''),
            'email'      => (string) ($r['buyer_email'] ?? ''),
            'phone'      => '',
        );

        $line_items = array();
        foreach ((array) ($r['transactions'] ?? array()) as $t) {
            if (!is_array($t)) { continue; }
            $line_items[] = array(
                'remote_id'  => (string) ($t['sku'] ?? ($t['listing_id'] ?? ($t['product_id'] ?? ''))),
                'qty'        => (int) ($t['quantity'] ?? 1),
                'unit_price' => $this->money($t['price'] ?? null),
                'tax'        => 0.0,
                'title'      => (string) ($t['title'] ?? ''),
            );
        }

        $status = (string) ($r['status'] ?? '');
        $cancelled = in_array($status, array('canceled', 'fully refunded', 'partially refunded'), true);
        $event_type = $cancelled ? 'order_cancelled' : ($status === 'completed' ? 'order_updated' : 'new_order');

        return array(
            'event_type'      => $event_type,
            'remote_order_id' => (string) ($r['receipt_id'] ?? ''),
            'remote_code'     => (string) ($r['receipt_id'] ?? ''),
            'state'           => $status !== '' ? $status : 'unknown',
            'billing'         => $address,
            'shipping'        => $address,
            'line_items'      => $line_items,
            'totals'          => array(
                'shipping' => $this->money($r['total_shipping_cost'] ?? null),
                'tax'      => $this->money($r['total_tax_cost'] ?? null) + $this->money($r['total_vat_cost'] ?? null),
                'fees'     => 0.0,
                'total'    => $this->money($r['grandtotal'] ?? ($r['total_price'] ?? null)),
            ),
            'courier'         => '',
            'voucher_url'     => '',
            'tracking_codes'  => $this->tracking_codes($r),
            'invoice'         => null,
            'gift_wrap'       => !empty($r['is_gift']),
            'raw'             => $r,
            'created_at'      => (int) ($r['created_timestamp'] ?? ($r['create_timestamp'] ?? 0)),
        );
    }

    
    private function money($m) {
        if (!is_array($m) || !isset($m['amount'], $m['divisor']) || (int) $m['divisor'] === 0) { return 0.0; }
        return round((float) $m['amount'] / (float) $m['divisor'], 4);
    }

    private function tracking_codes(array $r) {
        $out = array();
        foreach ((array) ($r['shipments'] ?? array()) as $s) {
            if (!empty($s['tracking_code'])) { $out[] = (string) $s['tracking_code']; }
        }
        return $out;
    }

    private function split_name($name) {
        $name = trim((string) $name);
        if ($name === '') { return array('', ''); }
        $parts = preg_split('/\s+/', $name, 2);
        return array($parts[0], isset($parts[1]) ? $parts[1] : '');
    }

    

    
    private function access_token(array $vendor) {
        return AIEO_MOI_OAuth::access_token($vendor, function ($oauth_state, $creds) {
            $keystring = (string) ($creds['keystring'] ?? '');
            $refresh   = AIEO_MOI_OAuth::refresh_token($oauth_state, $creds, 'refresh_token');
            if ($keystring === '' || $refresh === '') {
                return new WP_Error('etsy_no_refresh', __('Etsy keystring + refresh token are required (run the OAuth connect once).', 'ai-eshop-optimizer'));
            }
            return AIEO_MOI_OAuth::token_request(self::TOKEN_URL, array(
                'grant_type'    => 'refresh_token',
                'client_id'     => $keystring,
                'refresh_token' => $refresh,
            ));
        });
    }

    
    private function api_get(array $vendor, $path, array $query = array()) {
        $token = $this->access_token($vendor);
        if (is_wp_error($token)) { return $token; }
        $keystring = (string) ($vendor['credentials']['keystring'] ?? '');

        $url = self::API_BASE . $path;
        if (!empty($query)) { $url = add_query_arg($query, $url); }

        $resp = wp_remote_get($url, array(
            'timeout' => 25,
            'headers' => array(
                'x-api-key'     => $keystring,
                'Authorization' => 'Bearer ' . $token,
                'Accept'        => 'application/json',
            ),
        ));
        if (is_wp_error($resp)) { return $resp; }
        $code = (int) wp_remote_retrieve_response_code($resp);
        $raw  = (string) wp_remote_retrieve_body($resp);
        $json = json_decode($raw, true);
        if ($code >= 400) {
            $msg = is_array($json) && !empty($json['error']) ? (string) $json['error']
                
                : sprintf(__('Etsy API HTTP %d', 'ai-eshop-optimizer'), $code);
            return new WP_Error('etsy_api_error', $msg, array('status' => $code, 'body' => $raw));
        }
        if (!is_array($json)) {
            return new WP_Error('etsy_bad_json', __('Non-JSON response from Etsy.', 'ai-eshop-optimizer'));
        }
        return $json;
    }
}

AIEO_MOI_Provider_Registry::register(new AIEO_MOI_Provider_Etsy());
