<?php

declare(strict_types=1);

namespace Instinctor\Services;

use Instinctor\Core\Config;
use Instinctor\Core\DB;


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

/**
 * AfterShip Integration Service
 * Auto-updates shipment tracking information via AfterShip API
 */
class AfterShipService
{
    private const API_BASE = 'https://api.aftership.com/v4';
    
    private static ?array $settings = null;
    
    /**
     * Get AfterShip settings
     */
    public static function getSettings(): array
    {
        if (self::$settings === null) {
            self::$settings = Config::get('instinctor_aftership', [
                'enabled' => false,
                'api_key' => '',
                'update_frequency' => 'hourly', // hourly, twicedaily, daily
                'auto_detect_carrier' => true,
            ]);
        }
        return self::$settings;
    }
    
    /**
     * Check if AfterShip is enabled and configured
     */
    public static function isEnabled(): bool
    {
        $settings = self::getSettings();
        return !empty($settings['enabled']) && !empty($settings['api_key']);
    }
    
    /**
     * Save settings
     */
    public static function saveSettings(array $data): bool
    {
        $settings = [
            'enabled' => !empty($data['enabled']),
            'api_key' => sanitize_text_field($data['api_key'] ?? ''),
            'update_frequency' => in_array($data['update_frequency'] ?? '', ['hourly', 'twicedaily', 'daily']) 
                ? $data['update_frequency'] 
                : 'hourly',
            'auto_detect_carrier' => !empty($data['auto_detect_carrier']),
        ];
        
        self::$settings = null; // Clear cache
        
        // Schedule or unschedule cron
        if ($settings['enabled'] && $settings['api_key']) {
            self::scheduleCron($settings['update_frequency']);
        } else {
            self::unscheduleCron();
        }
        
        return Config::set('instinctor_aftership', $settings);
    }
    
    /**
     * Schedule tracking update cron
     */
    public static function scheduleCron(string $frequency = 'hourly'): void
    {
        self::unscheduleCron();
        
        if (!wp_next_scheduled('instinctor_aftership_update')) {
            wp_schedule_event(time(), $frequency, 'instinctor_aftership_update');
        }
    }
    
    /**
     * Unschedule tracking update cron
     */
    public static function unscheduleCron(): void
    {
        $timestamp = wp_next_scheduled('instinctor_aftership_update');
        if ($timestamp) {
            wp_unschedule_event($timestamp, 'instinctor_aftership_update');
        }
    }
    
    /**
     * Make API request to AfterShip
     */
    private static function apiRequest(string $method, string $endpoint, array $data = []): ?array
    {
        $settings = self::getSettings();
        
        if (empty($settings['api_key'])) {
            return null;
        }
        
        $args = [
            'method' => $method,
            'headers' => [
                'aftership-api-key' => $settings['api_key'],
                'Content-Type' => 'application/json',
            ],
            'timeout' => 30,
        ];
        
        if (!empty($data) && in_array($method, ['POST', 'PUT'])) {
            $args['body'] = json_encode($data);
        }
        
        $url = self::API_BASE . $endpoint;
        
        $response = wp_remote_request($url, $args);
        
        if (is_wp_error($response)) {
            // phpcs:disable WordPress.PHP.DevelopmentFunctions -- server-side error capture / one-time migration logging, not debug output.
            error_log('Instinctor AfterShip API error: ' . $response->get_error_message());
            // phpcs:enable WordPress.PHP.DevelopmentFunctions
            return null;
        }
        
        $body = json_decode(wp_remote_retrieve_body($response), true);
        
        if (wp_remote_retrieve_response_code($response) >= 400) {
            // phpcs:disable WordPress.PHP.DevelopmentFunctions -- server-side error capture / one-time migration logging, not debug output.
            error_log('Instinctor AfterShip API error: ' . ($body['meta']['message'] ?? 'Unknown error'));
            // phpcs:enable WordPress.PHP.DevelopmentFunctions
            return null;
        }
        
        return $body;
    }
    
    /**
     * Create tracking in AfterShip
     */
    public static function createTracking(string $trackingNumber, string $carrier = '', array $metadata = []): ?array
    {
        $data = [
            'tracking' => [
                'tracking_number' => $trackingNumber,
            ]
        ];
        
        if ($carrier) {
            // Map our carrier codes to AfterShip slugs
            $data['tracking']['slug'] = self::mapCarrierToSlug($carrier);
        }
        
        if (!empty($metadata['order_id'])) {
            $data['tracking']['order_id'] = (string)$metadata['order_id'];
        }
        
        if (!empty($metadata['customer_name'])) {
            $data['tracking']['customer_name'] = $metadata['customer_name'];
        }
        
        if (!empty($metadata['order_number'])) {
            $data['tracking']['order_id_path'] = $metadata['order_number'];
        }
        
        if (!empty($metadata['emails'])) {
            $data['tracking']['emails'] = (array)$metadata['emails'];
        }
        
        return self::apiRequest('POST', '/trackings', $data);
    }
    
    /**
     * Get tracking info from AfterShip
     */
    public static function getTracking(string $trackingNumber, string $carrier = ''): ?array
    {
        $slug = $carrier ? self::mapCarrierToSlug($carrier) : '';
        
        // If no carrier specified, try to detect
        if (!$slug) {
            $detected = self::detectCarrier($trackingNumber);
            if ($detected) {
                $slug = $detected[0]['slug'] ?? '';
            }
        }
        
        if (!$slug) {
            return null;
        }
        
        return self::apiRequest('GET', '/trackings/' . $slug . '/' . urlencode($trackingNumber));
    }
    
    /**
     * Detect carrier from tracking number
     */
    public static function detectCarrier(string $trackingNumber): ?array
    {
        $response = self::apiRequest('POST', '/couriers/detect', [
            'tracking' => [
                'tracking_number' => $trackingNumber
            ]
        ]);
        
        return $response['data']['couriers'] ?? null;
    }
    
    /**
     * Get list of supported carriers
     */
    public static function getCarriers(): ?array
    {
        $response = self::apiRequest('GET', '/couriers');
        return $response['data']['couriers'] ?? null;
    }
    
    /**
     * Map our carrier codes to AfterShip slugs
     */
    private static function mapCarrierToSlug(string $carrier): string
    {
        $map = [
            'usps' => 'usps',
            'ups' => 'ups',
            'fedex' => 'fedex',
            'dhl' => 'dhl',
            'amazon' => 'amazon',
            'ontrac' => 'ontrac',
            'lasership' => 'lasership',
            'canada_post' => 'canada-post',
            'royal_mail' => 'royal-mail',
        ];
        
        return $map[strtolower($carrier)] ?? $carrier;
    }
    
    /**
     * Map AfterShip status to our status
     */
    private static function mapStatus(string $aftershipStatus): string
    {
        $map = [
            'Pending' => 'pending',
            'InfoReceived' => 'label_created',
            'InTransit' => 'in_transit',
            'OutForDelivery' => 'out_for_delivery',
            'Delivered' => 'delivered',
            'AttemptFail' => 'failed',
            'AvailableForPickup' => 'out_for_delivery',
            'Exception' => 'failed',
            'Expired' => 'failed',
        ];
        
        return $map[$aftershipStatus] ?? 'in_transit';
    }
    
    /**
     * Update all pending trackings from AfterShip
     * Called by cron job
     */
    public static function updateAllTrackings(): int
    {
        if (!self::isEnabled()) {
            return 0;
        }
        
        $table = DB::prefix() . 'instinctor_order_tracking';
        
        // Get all non-delivered trackings
        $trackings = DB::get_results(
            "SELECT * FROM $table WHERE status NOT IN ('delivered', 'returned', 'failed') ORDER BY last_update ASC LIMIT 50"
        );
        
        $updated = 0;
        
        foreach ($trackings as $tracking) {
            $result = self::updateSingleTracking($tracking);
            if ($result) {
                $updated++;
            }
            
            // Rate limiting - small delay between API calls
            usleep(100000); // 100ms
        }
        
        return $updated;
    }
    
    /**
     * Update a single tracking from AfterShip
     */
    public static function updateSingleTracking(object $tracking): bool
    {
        $response = self::getTracking($tracking->tracking_number, $tracking->carrier);
        
        if (!$response || empty($response['data']['tracking'])) {
            return false;
        }
        
        $data = $response['data']['tracking'];
        
        $table = DB::prefix() . 'instinctor_order_tracking';
        
        $update = [
            'last_update' => current_time('mysql'),
        ];
        
        // Update status
        if (!empty($data['tag'])) {
            $newStatus = self::mapStatus($data['tag']);
            if ($newStatus !== $tracking->status) {
                $update['status'] = $newStatus;
                
                if ($newStatus === 'delivered' && !empty($data['delivery_time'])) {
                    // phpcs:disable WordPress.DateTime.RestrictedFunctions -- date() used intentionally for local-timezone formatting; gmdate() would force UTC and change behavior.
                    $update['delivered_at'] = date('Y-m-d H:i:s', strtotime($data['delivery_time']));
                    // phpcs:enable WordPress.DateTime.RestrictedFunctions
                }
            }
        }
        
        // Update estimated delivery
        if (!empty($data['expected_delivery'])) {
            // phpcs:disable WordPress.DateTime.RestrictedFunctions -- date() used intentionally for local-timezone formatting; gmdate() would force UTC and change behavior.
            $update['estimated_delivery'] = date('Y-m-d', strtotime($data['expected_delivery']));
            // phpcs:enable WordPress.DateTime.RestrictedFunctions
        }
        
        // Get latest checkpoint for status detail
        if (!empty($data['checkpoints']) && is_array($data['checkpoints'])) {
            $latestCheckpoint = end($data['checkpoints']);
            if (!empty($latestCheckpoint['message'])) {
                $update['status_detail'] = sanitize_text_field($latestCheckpoint['message']);
            }
        }
        
        // Update tracking URL if AfterShip provides one
        if (!empty($data['tracking_url'])) {
            $update['tracking_url'] = esc_url_raw($data['tracking_url']);
        }
        
        $result = DB::update($table, $update, ['id' => $tracking->id]);
        
        // Update order status if delivered
        if (!empty($update['status']) && $update['status'] === 'delivered') {
            DB::update(
                DB::prefix() . 'instinctor_orders',
                ['status' => 'completed'],
                ['id' => $tracking->order_id]
            );
        }
        
        return $result !== false;
    }
    
    /**
     * Hook into order tracking creation to also create in AfterShip
     */
    public static function onTrackingCreated(int $trackingId, array $data): void
    {
        $p = DB::prefix();
        if (!self::isEnabled()) {
            return;
        }
        
        
        // Get order info for metadata
        $order = DB::get_row(DB::prepare(
            "SELECT o.*, c.email, c.first_name, c.last_name 
             FROM {$p}instinctor_orders o
             LEFT JOIN {$p}instinctor_customers c ON o.customer_id = c.id
             WHERE o.id = %d",
            $data['order_id']
        ));
        
        $metadata = [
            'order_id' => $data['order_id'],
            'order_number' => $order->order_number ?? '',
            'customer_name' => trim(($order->first_name ?? '') . ' ' . ($order->last_name ?? '')),
            'emails' => $order->email ? [$order->email] : [],
        ];
        
        self::createTracking(
            $data['tracking_number'],
            $data['carrier'] ?? '',
            $metadata
        );
    }
    
    /**
     * Test API connection
     */
    public static function testConnection(): array
    {
        $response = self::apiRequest('GET', '/couriers');
        
        if ($response && isset($response['meta']['code']) && $response['meta']['code'] === 200) {
            return [
                'success' => true,
                'message' => 'Connection successful',
                'carriers_count' => count($response['data']['couriers'] ?? [])
            ];
        }
        
        return [
            'success' => false,
            'message' => $response['meta']['message'] ?? 'Connection failed'
        ];
    }
}
