<?php

namespace App\Services\HubSpot;

use HubSpot\Client\Crm\Lists\Model\ListCreateRequest;
use HubSpot\Client\Crm\Lists\Model\ListSearchRequest;
use HubSpot\Client\Crm\Lists\Model\ListFilterBranch;
use Illuminate\Support\Facades\Log;

/**
 * HubSpot List Service
 *
 * Handles all list-related operations in HubSpot including
 * creating, reading, and deleting contact lists.
 */
class HubSpotListService
{
    /**
     * @var HubSpotApiClient
     */
    protected $apiClient;

    /**
     * Create a new HubSpotListService instance.
     *
     * @param HubSpotApiClient $apiClient
     */
    public function __construct(HubSpotApiClient $apiClient)
    {
        $this->apiClient = $apiClient;
    }

    /**
     * Get all lists from HubSpot using search.
     *
     * @param int $limit Maximum number of lists to retrieve per page
     * @return array
     */
    public function getAllLists(int $limit = 100): array
    {
        return $this->apiClient->executeWithRetry(function () use ($limit) {
            $client = $this->apiClient->getClient();
            $lists = [];
            $offset = 0;

            do {
                // Use doSearch to get all lists with pagination
                $searchRequest = new ListSearchRequest([
                    'processing_types' => ['MANUAL', 'DYNAMIC'],
                    'offset' => $offset,
                    'count' => $limit,
                ]);

                $response = $client->crm()->lists()->listsApi()->doSearch($searchRequest);
                $listsPage = $response->getLists() ?? [];
                $lists = array_merge($lists, $listsPage);

                // Check if there are more pages
                $total = $response->getTotal() ?? 0;
                $offset += count($listsPage);

                if (count($listsPage) > 0 && $offset < $total) {
                    $this->apiClient->applyRateLimit();
                }
            } while (count($listsPage) > 0 && $offset < $total);

            return $lists;
        }, 'getAllLists');
    }

    /**
     * Get all server-sync lists (committees/workgroups with the configured suffix).
     *
     * @return array Array of list objects with 'listId', 'name', 'size'
     */
    public function getAllServerSyncLists(): array
    {
        $allLists = $this->getAllLists();
        $serverSyncLists = [];

        foreach ($allLists as $list) {
            $name = $list->getName();
            if ($this->apiClient->isServerSyncList($name)) {
                $serverSyncLists[] = [
                    'listId' => $list->getListId(),
                    'name' => $name,
                    'title' => $this->apiClient->extractTitleFromListName($name),
                    'size' => 0, // Size not available in search results
                    'processingType' => $list->getProcessingType(),
                    'listType' => 'server',
                ];
            }
        }

        return $serverSyncLists;
    }

    /**
     * Get all member-sync lists (member lists with the configured member suffix).
     *
     * @return array Array of list objects with 'listId', 'name', 'size'
     */
    public function getAllMemberSyncLists(): array
    {
        $allLists = $this->getAllLists();
        $memberSyncLists = [];

        foreach ($allLists as $list) {
            $name = $list->getName();
            if ($this->apiClient->isMemberSyncList($name)) {
                $memberSyncLists[] = [
                    'listId' => $list->getListId(),
                    'name' => $name,
                    'title' => $this->apiClient->extractTitleFromListName($name),
                    'size' => 0, // Size not available in search results
                    'processingType' => $list->getProcessingType(),
                    'listType' => 'member',
                ];
            }
        }

        return $memberSyncLists;
    }

    /**
     * Get all server-managed lists (both server-sync and member-sync).
     *
     * @return array Array of list objects with 'listId', 'name', 'size'
     */
    public function getAllManagedLists(): array
    {
        $allLists = $this->getAllLists();
        $managedLists = [];

        foreach ($allLists as $list) {
            $name = $list->getName();
            if ($this->apiClient->isAnyServerManagedList($name)) {
                $listType = $this->apiClient->isMemberSyncList($name) ? 'member' : 'server';
                $managedLists[] = [
                    'listId' => $list->getListId(),
                    'name' => $name,
                    'title' => $this->apiClient->extractTitleFromListName($name),
                    'size' => 0, // Size not available in search results
                    'processingType' => $list->getProcessingType(),
                    'listType' => $listType,
                ];
            }
        }

        return $managedLists;
    }

    /**
     * Get a list by its exact name.
     *
     * @param string $name The exact list name to search for
     * @return array|null
     */
    public function getListByName(string $name): ?array
    {
        return $this->apiClient->executeWithRetry(function () use ($name) {
            $client = $this->apiClient->getClient();

            // Search for list by name
            $searchRequest = new ListSearchRequest([
                'query' => $name,
                'processing_types' => ['MANUAL'],
            ]);

            $response = $client->crm()->lists()->listsApi()->search($searchRequest);
            $lists = $response->getLists() ?? [];

            // Find exact match
            foreach ($lists as $list) {
                if ($list->getName() === $name) {
                    return [
                        'listId' => $list->getListId(),
                        'name' => $list->getName(),
                        'title' => $this->apiClient->extractTitleFromListName($list->getName()),
                        'size' => $list->getSize() ?? 0,
                        'processingType' => $list->getProcessingType(),
                    ];
                }
            }

            return null;
        }, 'getListByName: ' . $name);
    }

    /**
     * Get a list by committee or workgroup title.
     *
     * @param string $title The committee/workgroup title
     * @return array|null
     */
    public function getListByTitle(string $title): ?array
    {
        $listName = $this->apiClient->buildListName($title);
        return $this->getListByName($listName);
    }

    /**
     * Get a list by its ID.
     *
     * @param string $listId
     * @return array|null
     */
    public function getListById(string $listId): ?array
    {
        return $this->apiClient->executeWithRetry(function () use ($listId) {
            $client = $this->apiClient->getClient();

            try {
                $list = $client->crm()->lists()->listsApi()->getById($listId, false);

                return [
                    'listId' => $list->getListId(),
                    'name' => $list->getName(),
                    'title' => $this->apiClient->extractTitleFromListName($list->getName()),
                    'size' => $list->getSize() ?? 0,
                    'processingType' => $list->getProcessingType(),
                ];
            } catch (\Exception $e) {
                if (strpos($e->getMessage(), '404') !== false) {
                    return null;
                }
                throw $e;
            }
        }, 'getListById: ' . $listId);
    }

    /**
     * Create a new static contact list.
     *
     * @param string $title The committee/workgroup/member title
     * @param bool $isMemberList Whether this is a member list (uses different suffix)
     * @return array|null The created list info or null if dry-run
     */
    public function createList(string $title, bool $isMemberList = false): ?array
    {
        $listName = $this->apiClient->buildListName($title, $isMemberList);

        if ($this->apiClient->isDryRun()) {
            Log::info("[DRY-RUN] Would create HubSpot list: {$listName}");
            return [
                'listId' => 'dry-run-' . md5($listName),
                'name' => $listName,
                'title' => $title,
                'size' => 0,
                'processingType' => 'MANUAL',
                'dryRun' => true,
                'listType' => $isMemberList ? 'member' : 'server',
            ];
        }

        return $this->apiClient->executeWithRetry(function () use ($listName, $title, $isMemberList) {
            $client = $this->apiClient->getClient();

            // Create a static (manual) list
            $createRequest = new ListCreateRequest([
                'name' => $listName,
                'object_type_id' => '0-1', // Contacts
                'processing_type' => 'MANUAL',
            ]);

            $response = $client->crm()->lists()->listsApi()->create($createRequest);

            // Response contains getList() which returns the created list object
            $list = $response->getList();

            Log::info("Created HubSpot list: {$listName} (ID: {$list->getListId()})");

            $this->apiClient->applyRateLimit();

            return [
                'listId' => $list->getListId(),
                'name' => $list->getName(),
                'title' => $title,
                'size' => $list->getSize() ?? 0,
                'processingType' => $list->getProcessingType(),
                'listType' => $isMemberList ? 'member' : 'server',
            ];
        }, 'createList: ' . $listName);
    }

    /**
     * Delete a list by ID.
     *
     * @param string $listId
     * @return bool
     */
    public function deleteList(string $listId): bool
    {
        if ($this->apiClient->isDryRun()) {
            Log::info("[DRY-RUN] Would delete HubSpot list ID: {$listId}");
            return true;
        }

        return $this->apiClient->executeWithRetry(function () use ($listId) {
            $client = $this->apiClient->getClient();
            $client->crm()->lists()->listsApi()->remove($listId);

            Log::info("Deleted HubSpot list ID: {$listId}");

            $this->apiClient->applyRateLimit();

            return true;
        }, 'deleteList: ' . $listId);
    }

    /**
     * Delete a list by title.
     *
     * @param string $title The committee/workgroup title
     * @return bool
     */
    public function deleteListByTitle(string $title): bool
    {
        $list = $this->getListByTitle($title);

        if ($list === null) {
            Log::warning("Cannot delete list: No list found for title '{$title}'");
            return false;
        }

        return $this->deleteList($list['listId']);
    }

    /**
     * Ensure a list exists for the given title, create if not.
     *
     * @param string $title
     * @return array The list info (existing or newly created)
     */
    public function ensureListExists(string $title): array
    {
        $existing = $this->getListByTitle($title);

        if ($existing !== null) {
            return $existing;
        }

        return $this->createList($title);
    }

    /**
     * Get mapping of server-sync lists indexed by title.
     *
     * @param string|null $listType Filter by list type: 'server', 'member', or null for all managed lists
     * @return array ['title' => ['listId' => ..., 'name' => ..., ...]]
     */
    public function getServerSyncListsMap(?string $listType = null): array
    {
        if ($listType === 'server') {
            $lists = $this->getAllServerSyncLists();
        } elseif ($listType === 'member') {
            $lists = $this->getAllMemberSyncLists();
        } else {
            $lists = $this->getAllManagedLists();
        }

        $map = [];

        foreach ($lists as $list) {
            $map[$list['title']] = $list;
        }

        return $map;
    }
}
