<?php

namespace App\Services\Courier\Providers;

use App\Contracts\CourierProviderInterface;
use App\Exceptions\CourierException;
use Illuminate\Support\Facades\Http;

class SteadFastCourierProvider implements CourierProviderInterface
{
    public function providerKey(): string
    {
        return 'steadfast';
    }

    public function bookParcel(array $payload, array $config): array
    {
        $response = $this->client($config)->post($this->url($config, 'booking_path'), $payload);
        $data = $response->json() ?? [];

        $consignment = data_get($data, 'consignment', []);
        $success = $response->successful() && (
            data_get($data, 'status') == 200 ||
            filled(data_get($consignment, 'consignment_id'))
        );

        return [
            'success' => $success,
            'http_status' => $response->status(),
            'message' => data_get($data, 'message') ?? ($success ? 'Consignment has been created successfully.' : 'Courier booking failed.'),
            'parcel_id' => (string) (data_get($consignment, 'consignment_id') ?? ''),
            'consignment_id' => (string) (data_get($consignment, 'consignment_id') ?? ''),
            'tracking_id' => (string) (data_get($consignment, 'tracking_code') ?? ''),
            'courier_status' => data_get($consignment, 'status'),
            'raw' => $data,
        ];
    }

    public function fetchTrackingStatus(array $trackingData, array $config): array
    {
        $response = $this->client($config)->get($this->trackingUrl($trackingData, $config));

        return $this->parseTrackingResponse(
            body: $response->body(),
            httpStatus: $response->status(),
            httpSuccessful: $response->successful()
        );
    }

    private function trackingUrl(array $trackingData, array $config): string
    {
        $consignmentId = trim((string) ($trackingData['consignment_id'] ?? ''));
        $trackingCode = trim((string) ($trackingData['tracking_code'] ?? ''));
        $invoice = trim((string) ($trackingData['invoice'] ?? ''));

        if ($consignmentId !== '') {
            return $this->replacePlaceholder(
                $this->url($config, 'status_by_consignment_path'),
                'consignment_id',
                $consignmentId
            );
        }

        if ($trackingCode !== '') {
            return $this->replacePlaceholder(
                $this->url($config, 'status_by_tracking_path'),
                'tracking_code',
                $trackingCode
            );
        }

        if ($invoice !== '') {
            return $this->replacePlaceholder(
                $this->url($config, 'status_by_invoice_path'),
                'invoice',
                $invoice
            );
        }

        throw new CourierException('No valid tracking identifier found for courier sync.');
    }

    private function parseTrackingResponse(string $body, int $httpStatus, bool $httpSuccessful): array
    {
        $decoded = json_decode($body, true);
        $isJsonArray = is_array($decoded);
        $data = $isJsonArray ? $decoded : [];
        $deliveryStatus = $this->deliveryStatus($data);
        $success = $httpSuccessful && $isJsonArray && $deliveryStatus !== '';

        $message = match (true) {
            ! $httpSuccessful => 'Courier status request failed.',
            ! $isJsonArray => 'Courier status response is not valid JSON.',
            $deliveryStatus === '' => 'Courier status response is incomplete.',
            default => 'Courier status synced successfully.',
        };

        return [
            'success' => $success,
            'http_status' => $httpStatus,
            'message' => $message,
            'courier_status' => $success ? $deliveryStatus : null,
            'raw' => $data,
            'masked_raw' => $isJsonArray
                ? $this->maskResponsePayload($data)
                : ['body' => $this->maskRawBody($body)],
        ];
    }

    private function deliveryStatus(array $data): string
    {
        if (! array_key_exists('delivery_status', $data) || ! is_scalar($data['delivery_status'])) {
            return '';
        }

        return trim((string) $data['delivery_status']);
    }

    private function maskResponsePayload(array $payload): array
    {
        $sensitiveKeys = [
            'api_key',
            'secret_key',
            'authorization',
            'token',
            'phone',
            'mobile',
            'address',
            'tracking_code',
            'tracking_id',
            'consignment_id',
        ];

        foreach ($payload as $key => $value) {
            $normalizedKey = strtolower(str_replace('-', '_', (string) $key));

            if (in_array($normalizedKey, $sensitiveKeys, true)) {
                $payload[$key] = '[masked]';
                continue;
            }

            if (is_array($value)) {
                $payload[$key] = $this->maskResponsePayload($value);
            }
        }

        return $payload;
    }

    private function maskRawBody(string $body): string
    {
        $body = substr($body, 0, 4000);

        return (string) preg_replace(
            '/("(?:api[-_]?key|secret[-_]?key|authorization|token|phone|mobile|address|tracking[-_]?code|tracking[-_]?id|consignment[-_]?id)"\s*:\s*)"[^"]*"/i',
            '$1"[masked]"',
            $body
        );
    }

    private function client(array $config)
    {
        $apiKey = trim((string) ($config['api_key'] ?? ''));
        $secretKey = trim((string) ($config['secret_key'] ?? ''));

        if (blank($apiKey) || blank($secretKey)) {
            throw new CourierException('Courier API key/secret missing. আগে settings থেকে credential save করতে হবে।');
        }

        return Http::withHeaders([
            'Api-Key' => $apiKey,
            'Secret-Key' => $secretKey,
            'Content-Type' => 'application/json',
        ])->timeout(20)->acceptJson();
    }

    private function url(array $config, string $pathKey): string
    {
        $baseUrl = rtrim((string) ($config['base_url'] ?? ''), '/');
        $path = trim((string) ($config[$pathKey] ?? ''), ' ');

        if (blank($baseUrl) || blank($path)) {
            throw new CourierException('Courier base URL or endpoint path missing in settings.');
        }

        return $baseUrl . '/' . ltrim($path, '/');
    }

    private function replacePlaceholder(string $url, string $key, string $value): string
    {
        return str_replace('{' . $key . '}', rawurlencode($value), $url);
    }
}
