<?php

namespace PloverCRM\Core\Api\V1;

use PloverCRM\Common\CrmUserManager;
use PloverCRM\Core\Api\AbstractEndpoint;
use PloverCRM\Common\Permissions;
use PloverCRM\Common\RoleManager;
use WP_REST_Request;

/**
 * Contacts Endpoint
 * 
 * GET    /wp-json/plover-crm/v1/contacts
 * GET    /wp-json/plover-crm/v1/contacts/{id}
 * POST   /wp-json/plover-crm/v1/contacts
 * PUT    /wp-json/plover-crm/v1/contacts/{id}
 * DELETE /wp-json/plover-crm/v1/contacts/{id}
 * POST   /wp-json/plover-crm/v1/contacts/{id}/claim
 */
class ContactsEndpoint extends AbstractEndpoint
{
    public function getRoute(): string
    {
        return '/contacts(?:/(?P<id>\d+)(?:/(?P<action>claim))?)?';
    }

    public function getMethod()
    {
        return ['GET', 'POST', 'PUT', 'DELETE'];
    }

    public function handle(WP_REST_Request $request)
    {
        $crm = $this->container->get('crm');

        if (!$crm->isAvailable()) {
            return $this->error('CRM service is currently unavailable', 503, 'crm_unavailable');
        }

        $method = $request->get_method();
        $id = $request->get_param('id');
        $action = $request->get_param('action');

        // Handle claim action
        if ($action === 'claim' && $method === 'POST') {
            return $this->claimContact($crm, $id);
        }

        switch ($method) {
            case 'GET':
                return $id ? $this->getContact($crm, $id, $request) : $this->listContacts($crm, $request);

            case 'POST':
                return $this->createContact($crm, $request);

            case 'PUT':
                return $this->updateContact($crm, $id, $request);

            case 'DELETE':
                return $this->deleteContact($crm, $id);

            default:
                return $this->error('The requested method is not supported for this endpoint', 405);
        }
    }

    /**
     * List contacts with ownership filtering
     */
    private function listContacts($crm, WP_REST_Request $request)
    {
        $user = wp_get_current_user();
        $currentActor = CrmUserManager::getPublicProfileByWordPressUserId((int) $user->ID);
        $readableFields = RoleManager::getReadableFields($user->ID);

        if (empty($readableFields)) {
            return $this->error('You do not have permission to view any contact fields', 403, 'no_read_permissions');
        }

        if (!$currentActor) {
            return $this->error('Unable to resolve the current CRM user', 401, 'user_not_found');
        }

        $page = $request->get_param('page') ?? 1;
        $per_page = $request->get_param('per_page') ?? 20;
        $search = $request->get_param('search') ?? '';
        $owner_filter = $request->get_param('owner'); // 'me', 'unassigned', crm_user_id, or null for all

        $args = [
            'page' => max(1, intval($page)),
            'per_page' => min(100, max(1, intval($per_page))),
            'search' => sanitize_text_field($search),
        ];

        // Apply ownership filtering based on user's view_scope
        $viewScope = RoleManager::getViewScope($user->ID);

        if ($viewScope === RoleManager::SCOPE_OWNED) {
            // User can only see their own contacts + unassigned
            $args['owner_id'] = $user->ID;
            $args['include_unassigned'] = true;
        } elseif ($owner_filter !== null) {
            // User has view_scope=all but wants to filter
            if ($owner_filter === 'me') {
                $args['owner_id'] = $user->ID;
            } elseif ($owner_filter === 'unassigned') {
                $args['owner_id'] = null;
                $args['only_unassigned'] = true;
            } elseif (is_numeric($owner_filter)) {
                $ownerWpUserId = CrmUserManager::getWordPressUserIdByPublicId((int) $owner_filter);

                if (!$ownerWpUserId) {
                    return $this->error('The specified contact owner does not exist', 400, 'invalid_owner');
                }

                $args['owner_id'] = $ownerWpUserId;
            }
        }

        $result = $crm->getContactsByOwner($args);

        // Filter contacts based on user permissions and add ownership info
        $filteredContacts = array_map(function ($contact) use ($crm, $currentActor) {
            $filtered = Permissions::filterContactForUser($contact, 'read');
            $filtered = $this->addOwnershipInfo($filtered, $crm, (int) $currentActor['id']);
            return $filtered;
        }, $result['contacts']);

        return $this->success($filteredContacts, [
            'total' => $result['total'],
            'page' => $args['page'],
            'per_page' => $args['per_page'],
            'total_pages' => ceil($result['total'] / $args['per_page']),
        ]);
    }

    /**
     * Get single contact with ownership check
     */
    private function getContact($crm, $id, ?WP_REST_Request $request = null)
    {
        if (empty($id)) {
            return $this->error('Contact ID is required', 400, 'missing_contact_id');
        }

        $user = wp_get_current_user();
        $currentActor = CrmUserManager::getPublicProfileByWordPressUserId((int) $user->ID);
        $readableFields = RoleManager::getReadableFields($user->ID);

        if (empty($readableFields)) {
            return $this->error('You do not have permission to view any contact fields', 403, 'no_read_permissions');
        }

        if (!$currentActor) {
            return $this->error('Unable to resolve the current CRM user', 401, 'user_not_found');
        }

        $contactId = intval($id);
        $contact = $crm->getContact($contactId);

        if (!$contact) {
            return $this->error('The requested contact does not exist', 404, 'contact_not_found');
        }

        // Check ownership-based view permission
        $ownerId = $crm->getContactOwner($contactId);
        if (!Permissions::canViewContact($ownerId)) {
            return $this->error('You do not have permission to view this contact', 403, 'view_permission_denied');
        }

        // Filter contact based on user permissions
        $filteredContact = Permissions::filterContactForUser($contact, 'read');
        $filteredContact = $this->addOwnershipInfo($filteredContact, $crm, (int) $currentActor['id']);

        return $this->success($filteredContact);
    }

    /**
     * Create contact with auto-ownership assignment
     */
    private function createContact($crm, WP_REST_Request $request)
    {
        $data = $request->get_json_params();
        $user = wp_get_current_user();
        $currentActor = CrmUserManager::getPublicProfileByWordPressUserId((int) $user->ID);

        if (!$currentActor) {
            return $this->error('Unable to resolve the current CRM user', 401, 'user_not_found');
        }

        // Check if user has permission to create the required unique field
        if (!RoleManager::isSuperAdmin($user->ID)) {
            $uniqueFieldInfo = $crm->getRequiredUniqueField();
            $fieldToCheck = $uniqueFieldInfo['field'];

            if (!RoleManager::checkFieldPermission($user->ID, $fieldToCheck, 'create')) {
                return $this->error(
                    "You cannot create contacts because you do not have permission to set the required field ({$uniqueFieldInfo['label']})",
                    403,
                    'missing_required_field_permission'
                );
            }
        }

        // Filter data based on create permissions
        $filterResult = $this->filterDataByPermission($data, 'create');
        $data = $filterResult['data'];
        $ignoredFields = $filterResult['ignored_fields'];

        if (empty($data)) {
            return $this->error('You do not have permission to create contacts with the provided fields', 403, 'no_create_permissions');
        }

        $resolvedAssignedOwnerId = null;
        if (RoleManager::isSuperAdmin($user->ID) && array_key_exists('contact_owner', $data)) {
            $requestedOwner = $data['contact_owner'];

            if ($requestedOwner === null || $requestedOwner === '') {
                $resolvedAssignedOwnerId = null;
            } elseif ($requestedOwner === 'me') {
                $resolvedAssignedOwnerId = (int) $user->ID;
            } elseif (is_numeric($requestedOwner)) {
                $resolvedAssignedOwnerId = CrmUserManager::getWordPressUserIdByPublicId((int) $requestedOwner);

                if (!$resolvedAssignedOwnerId || !RoleManager::userHasAccess($resolvedAssignedOwnerId)) {
                    return $this->error('The specified user does not have CRM access', 400, 'invalid_owner');
                }
            } else {
                return $this->error('The specified contact owner is invalid', 400, 'invalid_owner');
            }
        }

        // Validate contact data using CRM provider
        $validation = $crm->validateContactData($data);
        if (!$validation['valid']) {
            return $this->error($validation['error_message'], 400, $validation['error_code']);
        }

        $contact = $crm->createOrUpdateContact($data);

        if (!$contact) {
            return $this->error('Unable to create contact. Please verify all required fields are provided correctly.', 500, 'contact_creation_failed');
        }

        // Auto-assign owner to creator (unless super_admin explicitly sets different owner)
        if (!RoleManager::isSuperAdmin($user->ID) || !array_key_exists('contact_owner', $data)) {
            $crm->setContactOwner($contact['id'], $user->ID);
        } else {
            $crm->setContactOwner($contact['id'], $resolvedAssignedOwnerId);
        }

        // Refresh contact to get owner info
        $contact = $crm->getContact($contact['id']);

        // Filter response based on read permissions
        $filteredContact = Permissions::filterContactForUser($contact, 'read');
        $filteredContact = $this->addOwnershipInfo($filteredContact, $crm, (int) $currentActor['id']);

        $meta = ['created' => true];
        if (!empty($ignoredFields)) {
            $meta['ignored_fields'] = $ignoredFields;
        }

        return $this->success($filteredContact, $meta, 201);
    }

    /**
     * Update contact with ownership check
     */
    private function updateContact($crm, $id, WP_REST_Request $request)
    {
        if (empty($id)) {
            return $this->error('Contact ID is required', 400, 'missing_contact_id');
        }

        $data = $request->get_json_params();

        if (!is_array($data) || empty($data)) {
            return $this->error('Request body must contain valid contact data', 400, 'invalid_request_body');
        }

        $contactId = intval($id);
        $user = wp_get_current_user();
        $currentActor = CrmUserManager::getPublicProfileByWordPressUserId((int) $user->ID);

        if (!$currentActor) {
            return $this->error('Unable to resolve the current CRM user', 401, 'user_not_found');
        }

        // Check if contact exists
        $existing = $crm->getContact($contactId);
        if (!$existing) {
            return $this->error('The requested contact does not exist', 404, 'contact_not_found');
        }

        // Check ownership-based update permission
        $ownerId = $crm->getContactOwner($contactId);
        if (!Permissions::canUpdateContact($ownerId)) {
            return $this->error('You do not have permission to update this contact', 403, 'update_permission_denied');
        }

        // Handle contact_owner reassignment
        if (isset($data['contact_owner'])) {
            $newOwnerId = $data['contact_owner'];

            // Check if user can reassign
            if (!Permissions::canReassignContact($ownerId)) {
                return $this->error('You do not have permission to reassign this contact', 403, 'reassign_permission_denied');
            }

            // Validate new owner
            if ($newOwnerId !== null) {
                if ($newOwnerId === 'me') {
                    $newOwnerId = $user->ID;
                } else {
                    $newOwnerId = CrmUserManager::getWordPressUserIdByPublicId((int) $newOwnerId);
                    // Verify new owner has CRM access
                    if (!$newOwnerId || !RoleManager::userHasAccess($newOwnerId)) {
                        return $this->error('The specified user does not have CRM access', 400, 'invalid_owner');
                    }
                }
            }

            $crm->setContactOwner($contactId, $newOwnerId);
            unset($data['contact_owner']);
        }

        // If no other data to update, just return the contact
        if (empty($data)) {
            $contact = $crm->getContact($contactId);
            $filteredContact = Permissions::filterContactForUser($contact, 'read');
            $filteredContact = $this->addOwnershipInfo($filteredContact, $crm, (int) $currentActor['id']);
            return $this->success($filteredContact, ['updated' => true]);
        }

        // Filter data based on update permissions
        $filterResult = $this->filterDataByPermission($data, 'update');
        $data = $filterResult['data'];
        $ignoredFields = $filterResult['ignored_fields'];

        if (empty($data) || (count($data) === 1 && isset($data['id']))) {
            return $this->error('You do not have permission to update any of the provided fields', 403, 'insufficient_field_permissions');
        }

        // Add ID to data for update
        $data['id'] = $contactId;

        $contact = $crm->createOrUpdateContact($data);

        if (!$contact) {
            return $this->error('Unable to update contact', 500, 'contact_update_failed');
        }

        // Filter response based on read permissions
        $filteredContact = Permissions::filterContactForUser($contact, 'read');
        $filteredContact = $this->addOwnershipInfo($filteredContact, $crm, (int) $currentActor['id']);

        $meta = ['updated' => true];
        if (!empty($ignoredFields)) {
            $meta['ignored_fields'] = $ignoredFields;
        }

        return $this->success($filteredContact, $meta);
    }

    /**
     * Delete contact with ownership check
     */
    private function deleteContact($crm, $id)
    {
        if (empty($id)) {
            return $this->error('Contact ID is required', 400, 'missing_contact_id');
        }

        $contactId = intval($id);

        // Check if contact exists
        $existing = $crm->getContact($contactId);
        if (!$existing) {
            return $this->error('The requested contact does not exist', 404, 'contact_not_found');
        }

        // Check ownership-based delete permission
        $ownerId = $crm->getContactOwner($contactId);
        if (!Permissions::canDeleteContact($ownerId)) {
            return $this->error('You do not have permission to delete this contact', 403, 'delete_permission_denied');
        }

        $result = $crm->deleteContact($contactId);

        if (!$result) {
            return $this->error('Unable to delete contact', 500, 'contact_deletion_failed');
        }

        return $this->success(['deleted' => true]);
    }

    /**
     * Claim an unassigned contact
     */
    private function claimContact($crm, $id)
    {
        if (empty($id)) {
            return $this->error('Contact ID is required', 400, 'missing_contact_id');
        }

        $contactId = intval($id);
        $user = wp_get_current_user();
        $currentActor = CrmUserManager::getPublicProfileByWordPressUserId((int) $user->ID);

        if (!$currentActor) {
            return $this->error('Unable to resolve the current CRM user', 401, 'user_not_found');
        }

        // Check if contact exists
        $existing = $crm->getContact($contactId);
        if (!$existing) {
            return $this->error('The requested contact does not exist', 404, 'contact_not_found');
        }

        // Check if contact is unassigned
        $ownerId = $crm->getContactOwner($contactId);
        if (!Permissions::canClaimContact($ownerId)) {
            if ($ownerId !== null) {
                return $this->error('This contact is already assigned to another user', 400, 'contact_already_assigned');
            }
            return $this->error('You do not have permission to claim contacts', 403, 'claim_permission_denied');
        }

        // Assign to current user
        $crm->setContactOwner($contactId, $user->ID);

        // Get updated contact
        $contact = $crm->getContact($contactId);
        $filteredContact = Permissions::filterContactForUser($contact, 'read');
        $filteredContact = $this->addOwnershipInfo($filteredContact, $crm, (int) $currentActor['id']);

        return $this->success($filteredContact, ['claimed' => true]);
    }

    /**
     * Add ownership information to contact response
     */
    private function addOwnershipInfo(array $contact, $crm, int $currentActorId): array
    {
        $contactId = $contact['id'] ?? null;
        if (!$contactId) {
            return $contact;
        }

        $ownerId = $crm->getContactOwner($contactId);
        $ownerInfo = null;

        if ($ownerId !== null) {
            $ownerProfile = CrmUserManager::getPublicProfileByWordPressUserId((int) $ownerId);
            $ownerUser = get_user_by('ID', $ownerId);
            $hasAccess = RoleManager::userHasAccess($ownerId);

            $ownerInfo = [
                'id' => (int) ($ownerProfile['id'] ?? 0),
                'crm_user_id' => (int) ($ownerProfile['id'] ?? 0),
                'wp_user_id' => (int) $ownerId,
                'username' => $ownerProfile['username'] ?? ($ownerUser ? $ownerUser->user_login : ''),
                'email' => $ownerProfile['email'] ?? ($ownerUser ? $ownerUser->user_email : ''),
                'name' => $ownerProfile['display_name'] ?? ($ownerUser ? $ownerUser->display_name : 'Unknown User'),
                'is_current_user' => (int) ($ownerProfile['id'] ?? 0) === $currentActorId,
                'has_crm_access' => $hasAccess,
            ];
        }

        $contact['contact_owner'] = $ownerInfo;
        $contact['permissions'] = [
            'can_update' => Permissions::canUpdateContact($ownerId),
            'can_delete' => Permissions::canDeleteContact($ownerId),
            'can_reassign' => Permissions::canReassignContact($ownerId),
            'can_claim' => Permissions::canClaimContact($ownerId),
        ];

        return $contact;
    }

    /**
     * Filter data based on field permissions
     */
    private function filterDataByPermission(array $data, string $action): array
    {
        $user = wp_get_current_user();

        if (!$user || !$user->ID) {
            return ['data' => [], 'ignored_fields' => array_keys($data)];
        }

        // Super admin bypasses filtering
        if (RoleManager::isSuperAdmin($user->ID)) {
            return ['data' => $data, 'ignored_fields' => []];
        }

        $filtered = [];
        $ignoredFields = [];

        foreach ($data as $field => $value) {
            // Skip special fields
            if (in_array($field, ['lists', 'tags', 'contact_owner'])) {
                $filtered[$field] = $value;
                continue;
            }

            // Handle custom_fields specially
            if ($field === 'custom_fields' && is_array($value)) {
                $filteredCustomFields = [];
                $ignoredCustomFields = [];

                foreach ($value as $cfKey => $cfValue) {
                    if (RoleManager::checkFieldPermission($user->ID, $cfKey, $action)) {
                        $filteredCustomFields[$cfKey] = $cfValue;
                    } else {
                        $ignoredCustomFields[] = "custom_fields.{$cfKey}";
                    }
                }

                if (!empty($filteredCustomFields)) {
                    $filtered['custom_fields'] = $filteredCustomFields;
                }
                if (!empty($ignoredCustomFields)) {
                    $ignoredFields = array_merge($ignoredFields, $ignoredCustomFields);
                }
                continue;
            }

            if (RoleManager::checkFieldPermission($user->ID, $field, $action)) {
                $filtered[$field] = $value;
            } else {
                $ignoredFields[] = $field;
            }
        }

        return ['data' => $filtered, 'ignored_fields' => $ignoredFields];
    }
}
