<?php

namespace App\Extensions\Installed\Importer\Services\ApiAdapters;

use Illuminate\Support\Facades\Http;

class HelpScoutAdapter extends AbstractApiImportAdapter
{
    protected ?array $operatorIds = null;
    protected ?string $accessToken = null;
    protected ?int $tokenExpiry = null;

    public static function id(): string
    {
        return 'helpscout';
    }

    public static function label(): string
    {
        return 'HelpScout';
    }

    public static function defaults(): array
    {
        return [
            'chunk_size' => 50,
            'options' => [
                'import_departments' => true,
                'import_categories' => false,
                'import_users' => true,
                'import_operators' => true,
                'import_tickets' => true,
                'import_responses' => false,
                'import_articles' => false,
            ],
        ];
    }

    public static function isPreview(): bool
    {
        return false;
    }

    public function boot(array $apiConfig, array $options = []): void
    {
        parent::boot($apiConfig, $options);

        $clientId = trim((string) ($this->api['client_id'] ?? $this->api['api_key'] ?? ''));
        $clientSecret = trim((string) ($this->api['client_secret'] ?? $this->api['token'] ?? ''));

        if ($clientId === '') {
            throw new \InvalidArgumentException('HelpScout client_id (or api_key) is required');
        }

        if ($clientSecret === '') {
            throw new \InvalidArgumentException('HelpScout client_secret (or token) is required');
        }

        $this->api['base_url'] = 'https://api.helpscout.net/v2';
        $this->api['client_id'] = $clientId;
        $this->api['client_secret'] = $clientSecret;

        $this->obtainAccessToken();
    }

    public function stages(): array
    {
        return ['departments', 'users', 'tickets'];
    }

    public function count(string $stage): int
    {
        return match ($stage) {
            'departments' => $this->countMailboxes(),
            'users' => $this->countCustomers(),
            'tickets' => $this->countConversations(),
            default => 0,
        };
    }

    public function fetch(string $stage, int $offset, int $limit): array
    {
        $page = $this->pageFromOffset($offset, $limit);

        return match ($stage) {
            'departments' => $this->fetchMailboxes($page, $limit),
            'users' => $this->fetchCustomers($page, $limit),
            'tickets' => $this->fetchConversations($page, $limit),
            default => [],
        };
    }

    public function mapStatus(?int $statusId): string
    {
        return 'open';
    }

    public function mapPriority(?int $priorityId): string
    {
        return 'none';
    }

    public function isOperatorUser(int $userId): bool
    {
        if ($this->operatorIds === null) {
            $this->loadOperatorIds();
        }

        return in_array($userId, $this->operatorIds, true);
    }

    public function findUser(int $userId): ?array
    {
        try {
            $response = $this->http()->get("/customers/{$userId}");
            if ($response->successful()) {
                return $response->json();
            }
        } catch (\Exception $e) {
            // Fall through to try users endpoint
        }

        try {
            $response = $this->http()->get("/users/{$userId}");
            if ($response->successful()) {
                return $response->json();
            }
        } catch (\Exception $e) {
            return null;
        }

        return null;
    }

    protected function obtainAccessToken(): void
    {
        $response = Http::asForm()->post('https://api.helpscout.net/v2/oauth2/token', [
            'grant_type' => 'client_credentials',
            'client_id' => $this->api['client_id'],
            'client_secret' => $this->api['client_secret'],
        ]);

        if (!$response->successful()) {
            throw new \RuntimeException(
                'Failed to obtain HelpScout access token: ' . $response->body()
            );
        }

        $data = $response->json();
        $this->accessToken = $data['access_token'] ?? null;
        $this->tokenExpiry = time() + ((int) ($data['expires_in'] ?? 172800));

        if (!$this->accessToken) {
            throw new \RuntimeException('HelpScout API did not return an access token');
        }

        $this->api['headers'] = [
            'Authorization' => 'Bearer ' . $this->accessToken,
        ];
    }

    protected function loadOperatorIds(): void
    {
        try {
            $response = $this->http()->get('/users');
            if ($response->successful()) {
                $data = $response->json();
                $users = data_get($data, '_embedded.users', []);
                $this->operatorIds = array_map(fn($user) => (int) $user['id'], $users);
                return;
            }
        } catch (\Exception $e) {
            // Fall through to empty array
        }

        $this->operatorIds = [];
    }

    protected function pageFromOffset(int $offset, int $limit): int
    {
        return (int) floor($offset / max(1, $limit)) + 1;
    }

    protected function extractTotalCount(array $response): int
    {
        return (int) data_get($response, 'page.totalElements', 0);
    }

    protected function extractEmbeddedData(array $response, string $key): array
    {
        return data_get($response, "_embedded.{$key}", []);
    }

    protected function mapStatusString(string $status): string
    {
        return match (strtolower($status)) {
            'closed' => 'closed',
            'spam' => 'closed',
            'active', 'open', 'pending' => 'open',
            default => 'open',
        };
    }

    protected function countMailboxes(): int
    {
        try {
            $response = $this->http()->get('/mailboxes', ['page' => 1]);
            if ($response->successful()) {
                return $this->extractTotalCount($response->json());
            }
        } catch (\Exception $e) {
            return 0;
        }

        return 0;
    }

    protected function fetchMailboxes(int $page, int $perPage): array
    {
        $response = $this->http()->get('/mailboxes', ['page' => $page]);

        if (!$response->successful()) {
            return [];
        }

        $data = $response->json();
        $mailboxes = $this->extractEmbeddedData($data, 'mailboxes');

        $results = [];
        foreach ($mailboxes as $mailbox) {
            $results[] = [
                'id' => (int) $mailbox['id'],
                'name' => $mailbox['name'] ?? 'Unnamed Mailbox',
                'email' => $mailbox['email'] ?? null,
                'public' => true,
                'priority_enabled' => 1,
                'created_at' => $mailbox['createdAt'] ?? null,
                'updated_at' => $mailbox['updatedAt'] ?? null,
            ];
        }

        return $results;
    }

    protected function countCustomers(): int
    {
        try {
            $response = $this->http()->get('/customers', ['page' => 1]);
            if ($response->successful()) {
                return $this->extractTotalCount($response->json());
            }
        } catch (\Exception $e) {
            return 0;
        }

        return 0;
    }

    protected function fetchCustomers(int $page, int $perPage): array
    {
        $response = $this->http()->get('/customers', ['page' => $page]);

        if (!$response->successful()) {
            return [];
        }

        $data = $response->json();
        $customers = $this->extractEmbeddedData($data, 'customers');

        $results = [];
        foreach ($customers as $customer) {
            $firstName = trim((string) ($customer['firstName'] ?? ''));
            $lastName = trim((string) ($customer['lastName'] ?? ''));
            $name = trim("{$firstName} {$lastName}");

            $emails = data_get($customer, '_embedded.emails', []);
            $email = null;
            if (!empty($emails) && isset($emails[0]['value'])) {
                $email = $this->normalizeEmail($emails[0]['value']);
            }

            if (!$name && $email) {
                $name = explode('@', $email)[0];
            }

            if (!$name) {
                $name = 'Customer ' . $customer['id'];
            }

            $results[] = [
                'id' => (int) $customer['id'],
                'name' => $name,
                'email' => $email,
                'company' => $customer['organization'] ?? null,
                'confirmed' => 1,
                'created_at' => $customer['createdAt'] ?? null,
                'updated_at' => $customer['updatedAt'] ?? null,
            ];
        }

        return $results;
    }

    protected function countConversations(): int
    {
        try {
            $response = $this->http()->get('/conversations', [
                'status' => 'all',
                'page' => 1,
            ]);
            if ($response->successful()) {
                return $this->extractTotalCount($response->json());
            }
        } catch (\Exception $e) {
            return 0;
        }

        return 0;
    }

    protected function fetchConversations(int $page, int $perPage): array
    {
        $response = $this->http()->get('/conversations', [
            'status' => 'all',
            'page' => $page,
        ]);

        if (!$response->successful()) {
            return [];
        }

        $data = $response->json();
        $conversations = $this->extractEmbeddedData($data, 'conversations');

        $results = [];
        foreach ($conversations as $conversation) {
            $primaryCustomer = $conversation['primaryCustomer'] ?? [];
            $assignee = $conversation['assignee'] ?? null;

            $results[] = [
                'id' => (int) $conversation['id'],
                'number' => (string) ($conversation['number'] ?? $conversation['id']),
                'user_id' => (int) ($primaryCustomer['id'] ?? 0),
                'department_id' => (int) ($conversation['mailboxId'] ?? 0),
                'status' => $this->mapStatusString((string) ($conversation['status'] ?? 'active')),
                'priority' => 'none',
                'subject' => $conversation['subject'] ?? 'No Subject',
                'message' => $conversation['preview'] ?? '',
                'assigned_to' => $assignee ? (int) $assignee['id'] : null,
                'source' => data_get($conversation, 'source.type'),
                'cc_list' => $conversation['tags'] ?? [],
                'assignment_history' => [],
                'created_at' => $conversation['createdAt'] ?? null,
                'updated_at' => $conversation['userUpdatedAt'] ?? null,
                'closed_at' => $conversation['closedAt'] ?? null,
                'last_reply_at' => data_get($conversation, 'customerWaitingSince.time'),
            ];
        }

        return $results;
    }
}
