<?php

declare(strict_types=1);

use Exception;
use PDO;

class FormBuilderService
{
    /**
     * Available field types & metadata for the form builder UI
     */
    private array $fieldTypes = [
        'text' => [
            'name'       => 'Single Line Text',
            'icon'       => 'text',
            'validation' => ['max_length' => 255],
        ],
        'email' => [
            'name'       => 'Email Address',
            'icon'       => 'email',
            'validation' => ['email' => true],
        ],
        'phone' => [
            'name'       => 'Phone Number',
            'icon'       => 'phone',
            'validation' => ['phone' => true],
        ],
        'textarea' => [
            'name'       => 'Multi-line Text',
            'icon'       => 'textarea',
            'validation' => ['max_length' => 1000],
        ],
        'select' => [
            'name'       => 'Dropdown',
            'icon'       => 'dropdown',
            'validation' => [],
        ],
        'checkbox' => [
            'name'       => 'Checkbox',
            'icon'       => 'checkbox',
            'validation' => [],
        ],
        'radio' => [
            'name'       => 'Radio Buttons',
            'icon'       => 'radio',
            'validation' => [],
        ],
        'number' => [
            'name'       => 'Number',
            'icon'       => 'number',
            'validation' => ['number' => true],
        ],
        'date' => [
            'name'       => 'Date',
            'icon'       => 'date',
            'validation' => ['date' => true],
        ],
        'file' => [
            'name'       => 'File Upload',
            'icon'       => 'file',
            'validation' => ['file_types' => 'pdf,doc,docx,jpg,png'],
        ],
    ];

    /**
     * Expose field types to the UI / controller
     */
    public function getFieldTypes(): array
    {
        return $this->fieldTypes;
    }

    /**
     * iFrame embed code for a form
     */
    public function generateEmbedCode(string|int $formId, array $settings = []): string
    {
        $baseUrl  = $_ENV['APP_URL'] ?? 'http://localhost';
        $embedUrl = rtrim($baseUrl, '/') . '/lead-management/public/forms/embed/' . $formId;

        $defaultSettings = [
            'width'      => '100%',
            'height'     => '500px',
            'style'      => 'border: none;',
            'scrollable' => true,
        ];
        $settings  = array_merge($defaultSettings, $settings);
        $scrolling = !empty($settings['scrollable']) ? 'yes' : 'no';

        $html  = "<!-- LeadIntelligence Form Embed Code -->\n";
        $html .= '<div id="leadintelligence-form-' . htmlspecialchars((string) $formId) . '">' . "\n";
        $html .= '    <iframe src="' . htmlspecialchars($embedUrl) . '" '
            . 'width="' . htmlspecialchars((string) $settings['width']) . '" '
            . 'height="' . htmlspecialchars((string) $settings['height']) . '" '
            . 'style="' . htmlspecialchars((string) $settings['style']) . '" '
            . 'frameborder="0" '
            . 'scrolling="' . $scrolling . '"></iframe>' . "\n";
        $html .= "</div>\n";
        $html .= "<script>\n"
            . "(function(){\n"
            . "  var wrap = document.getElementById('leadintelligence-form-" . addslashes((string) $formId) . "');\n"
            . "  if(!wrap) return;\n"
            . "  var iframe = wrap.querySelector('iframe');\n"
            . "  window.addEventListener('message', function(event){\n"
            . "    if(event && event.data && event.data.type === 'leadintelligence-form-height'){\n"
            . "      iframe.style.height = String(event.data.height) + 'px';\n"
            . "    }\n"
            . "  });\n"
            . "})();\n"
            . "</script>\n"
            . "<!-- End LeadIntelligence Form -->\n";

        return $html;
    }

    /**
     * JS embed snippet that loads a script which then renders the form
     */
    public function generateDirectEmbedCode(string|int $formId): string
    {
        $baseUrl = $_ENV['APP_URL'] ?? 'http://localhost';
        $baseUrl = rtrim($baseUrl, '/');

        $id = htmlspecialchars((string) $formId);

        $html  = '<div id="leadintelligence-form-container-' . $id . '"></div>' . "\n";
        $html .= "<script>\n"
            . "(function(){\n"
            . "  var container = document.getElementById('leadintelligence-form-container-" . addslashes((string) $formId) . "');\n"
            . "  if(!container) return;\n"
            . "  var script = document.createElement('script');\n"
            . "  script.src = '" . addslashes($baseUrl) . "/lead-management/public/js/form-embed.js';\n"
            . "  script.setAttribute('data-form-id', '" . addslashes((string) $formId) . "');\n"
            . "  script.setAttribute('data-base-url', '" . addslashes($baseUrl) . "');\n"
            . "  container.appendChild(script);\n"
            . "})();\n"
            . "</script>\n";

        return $html;
    }

    /**
     * Validate the overall form structure payload coming from the builder UI
     */
    public function validateFormStructure(mixed $structure): array
    {
        if (!is_array($structure)) {
            return ['valid' => false, 'error' => 'Invalid form structure'];
        }

        foreach (['fields', 'settings', 'version'] as $key) {
            if (!array_key_exists($key, $structure)) {
                return ['valid' => false, 'error' => "Missing required key: {$key}"];
            }
        }

        foreach (($structure['fields'] ?? []) as $index => $field) {
            $validation = $this->validateField((array) $field, (int) $index);
            if (!$validation['valid']) {
                return $validation;
            }
        }

        return ['valid' => true];
    }

    /**
     * Validate a single field inside the form structure
     */
    private function validateField(array $field, int $index): array
    {
        foreach (['id', 'type', 'label'] as $key) {
            if (!isset($field[$key]) || $field[$key] === '') {
                return ['valid' => false, 'error' => "Field {$index} missing required property: {$key}"];
            }
        }

        if (!array_key_exists((string) $field['type'], $this->fieldTypes)) {
            return ['valid' => false, 'error' => "Field {$index} has invalid type: {$field['type']}"];
        }

        return ['valid' => true];
    }

    /**
     * Render a full HTML form from structure
     */
    public function renderForm(array $formStructure, ?array $submission = null): string
    {
        $fields   = $formStructure['fields'] ?? [];
        $settings = $formStructure['settings'] ?? [];

        $html  = '<form id="leadintelligence-form" method="POST" class="space-y-6">';
        $html .= '<input type="hidden" name="form_token" value="' . htmlspecialchars($this->generateFormToken()) . '">';

        foreach ($fields as $field) {
            $html .= $this->renderField((array) $field, $submission ?? []);
        }

        $html .= '<div class="form-submit">';
        $html .= '<button type="submit" class="w-full bg-blue-600 text-white py-3 px-4 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition duration-200">';
        $html .= htmlspecialchars((string) ($settings['submit_text'] ?? 'Submit'));
        $html .= '</button>';
        $html .= '</div>';
        $html .= '</form>';

        return $html;
    }

    /**
     * Render a single field as HTML
     */
    private function renderField(array $field, ?array $submission = null): string
    {
        $submission = $submission ?? [];
        $value       = $submission[$field['id']] ?? '';
        $required    = (bool) ($field['required'] ?? false);
        $placeholder = (string) ($field['placeholder'] ?? '');
        $classes     = 'mt-1 block w-full rounded-md border-gray-300 shadow-sm '
            . 'focus:border-blue-500 focus:ring-blue-500'
            . ($required ? ' required' : '');

        $html  = '<div class="form-field" data-field-type="' . htmlspecialchars((string) $field['type']) . '">';
        $html .= '<label for="' . htmlspecialchars((string) $field['id']) . '" class="block text-sm font-medium text-gray-700">';
        $html .= htmlspecialchars((string) $field['label']);
        if ($required) {
            $html .= ' <span class="text-red-500">*</span>';
        }
        $html .= '</label>';

        switch ($field['type']) {
            case 'text':
            case 'email':
            case 'phone':
            case 'number':
                $html .= '<input type="' . htmlspecialchars((string) $field['type']) . '" '
                    . 'id="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'name="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'value="' . htmlspecialchars((string) $value) . '" '
                    . 'placeholder="' . htmlspecialchars($placeholder) . '" '
                    . 'class="' . htmlspecialchars($classes) . '" ' . ($required ? 'required' : '') . '>';
                break;

            case 'textarea':
                $html .= '<textarea id="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'name="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'placeholder="' . htmlspecialchars($placeholder) . '" '
                    . 'class="' . htmlspecialchars($classes) . '" rows="4" ' . ($required ? 'required' : '') . '>'
                    . htmlspecialchars((string) $value) . '</textarea>';
                break;

            case 'select':
                $html .= '<select id="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'name="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'class="' . htmlspecialchars($classes) . '" ' . ($required ? 'required' : '') . '>';
                $html .= '<option value="">Select an option</option>';
                foreach ($field['options'] ?? [] as $option) {
                    $optVal    = (string) ($option['value'] ?? '');
                    $optLabel  = (string) ($option['label'] ?? $optVal);
                    $selected  = ((string) $value === $optVal) ? ' selected' : '';
                    $html     .= '<option value="' . htmlspecialchars($optVal) . '"' . $selected . '>'
                        . htmlspecialchars($optLabel) . '</option>';
                }
                $html .= '</select>';
                break;

            case 'checkbox':
                $html .= '<div class="space-y-2">';
                $vals = is_array($value) ? $value : ((string) $value === '' ? [] : [(string) $value]);
                foreach ($field['options'] ?? [] as $option) {
                    $optVal   = (string) ($option['value'] ?? '');
                    $optLabel = (string) ($option['label'] ?? $optVal);
                    $checked  = in_array($optVal, $vals, true) ? ' checked' : '';
                    $id       = htmlspecialchars((string) $field['id'] . '_' . $optVal);
                    $html    .= '<div class="flex items-center">';
                    $html    .= '<input type="checkbox" id="' . $id . '" '
                        . 'name="' . htmlspecialchars((string) $field['id']) . '[]" '
                        . 'value="' . htmlspecialchars($optVal) . '" '
                        . 'class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"' . $checked . '>';
                    $html    .= '<label for="' . $id . '" class="ml-2 text-sm text-gray-700">'
                        . htmlspecialchars($optLabel) . '</label>';
                    $html    .= '</div>';
                }
                $html .= '</div>';
                break;

            case 'radio':
                $html .= '<div class="space-y-2">';
                foreach ($field['options'] ?? [] as $option) {
                    $optVal   = (string) ($option['value'] ?? '');
                    $optLabel = (string) ($option['label'] ?? $optVal);
                    $checked  = ((string) $value === $optVal) ? ' checked' : '';
                    $id       = htmlspecialchars((string) $field['id'] . '_' . $optVal);
                    $html    .= '<div class="flex items-center">';
                    $html    .= '<input type="radio" id="' . $id . '" '
                        . 'name="' . htmlspecialchars((string) $field['id']) . '" '
                        . 'value="' . htmlspecialchars($optVal) . '" '
                        . 'class="rounded border-gray-300 text-blue-600 focus:ring-blue-500"'
                        . $checked . ($required ? ' required' : '') . '>';
                    $html    .= '<label for="' . $id . '" class="ml-2 text-sm text-gray-700">'
                        . htmlspecialchars($optLabel) . '</label>';
                    $html    .= '</div>';
                }
                $html .= '</div>';
                break;

            case 'date':
                $html .= '<input type="date" '
                    . 'id="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'name="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'value="' . htmlspecialchars((string) $value) . '" '
                    . 'class="' . htmlspecialchars($classes) . '" ' . ($required ? 'required' : '') . '>';
                break;

            case 'file':
                $accept = (string) ($field['accept'] ?? '.pdf,.doc,.docx,.jpg,.jpeg,.png');
                $html  .= '<input type="file" '
                    . 'id="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'name="' . htmlspecialchars((string) $field['id']) . '" '
                    . 'class="' . htmlspecialchars($classes) . '" '
                    . 'accept="' . htmlspecialchars($accept) . '" ' . ($required ? 'required' : '') . '>';
                break;
        }

        if (!empty($field['description'])) {
            $html .= '<p class="mt-1 text-sm text-gray-500">'
                . htmlspecialchars((string) $field['description'])
                . '</p>';
        }

        $html .= '</div>';
        return $html;
    }

    /**
     * Generate & store a CSRF-like token for forms
     */
    private function generateFormToken(): string
    {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            @session_start();
        }

        $_SESSION['form_tokens'] = $_SESSION['form_tokens'] ?? [];

        $token                          = bin2hex(random_bytes(32));
        $_SESSION['form_tokens'][$token] = time();

        // Clean up old tokens (older than 1 hour)
        foreach ($_SESSION['form_tokens'] as $storedToken => $timestamp) {
            if (time() - (int) $timestamp > 3600) {
                unset($_SESSION['form_tokens'][$storedToken]);
            }
        }

        return $token;
    }

    /**
     * Validate a form token once, and invalidate it
     */
    public function validateFormToken(string $token): bool
    {
        if (session_status() !== PHP_SESSION_ACTIVE) {
            @session_start();
        }

        if (!isset($_SESSION['form_tokens'][$token])) {
            return false;
        }

        $timestamp = (int) $_SESSION['form_tokens'][$token];
        unset($_SESSION['form_tokens'][$token]);

        return (time() - $timestamp) < 3600;
    }

    /**
     * Process a public form submission:
     *  - validate fields
     *  - store in form_submissions
     *  - increment submissions_count on forms
     *  - auto-create lead (and optionally AI-score it)
     */
    public function processSubmission(int $formId, array $submissionData): array
    {
        $db = Database::getConnection();

        try {
            // Get form structure
            $stmt = $db->prepare("SELECT structure, settings FROM forms WHERE id = ? AND tenant_id = ?");
            $stmt->execute([$formId, Auth::tenantId()]);
            $form = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$form) {
                throw new Exception('Form not found');
            }

            $structure = json_decode((string) $form['structure'], true) ?: [];
            $settings  = json_decode((string) $form['settings'], true) ?: [];

            // Validate required fields
            foreach (($structure['fields'] ?? []) as $field) {
                if (($field['required'] ?? false) && empty($submissionData[$field['id']] ?? null)) {
                    throw new Exception('Required field missing: ' . ($field['label'] ?? $field['id']));
                }
            }

            // Store submission
            $stmt = $db->prepare("
                INSERT INTO form_submissions 
                    (tenant_id, form_id, submission_data, ip_address, user_agent, page_url)
                VALUES (?, ?, ?, ?, ?, ?)
            ");

            $stmt->execute([
                Auth::tenantId(),
                $formId,
                json_encode($submissionData, JSON_UNESCAPED_UNICODE),
                $_SERVER['REMOTE_ADDR']      ?? null,
                $_SERVER['HTTP_USER_AGENT']  ?? null,
                $_SERVER['HTTP_REFERER']     ?? null,
            ]);

            $submissionId = (int) $db->lastInsertId();

            // Update form submissions count
            $stmt = $db->prepare("
                UPDATE forms 
                   SET submissions_count = submissions_count + 1 
                 WHERE id = ? AND tenant_id = ?
            ");
            $stmt->execute([$formId, Auth::tenantId()]);

            // Auto-create lead if enabled
            if (($settings['auto_create_lead'] ?? true) === true) {
                $this->createLeadFromSubmission($formId, $submissionId, $submissionData, $settings);
            }

            return [
                'success'       => true,
                'submission_id' => $submissionId,
                'message'       => (string) ($settings['success_message'] ?? 'Thank you for your submission!'),
            ];
        } catch (Exception $e) {
            error_log('Form submission error: ' . $e->getMessage());
            return [
                'success' => false,
                'error'   => $e->getMessage(),
            ];
        }
    }

    /**
     * Create a lead record from a form submission
     */
    private function createLeadFromSubmission(int $formId, int $submissionId, array $submissionData, array $settings): ?int
    {
        $db = Database::getConnection();

        try {
            $leadData = [
                'tenant_id'     => Auth::tenantId(),
                'form_id'       => $formId,
                'submission_id' => $submissionId,
                'source'        => 'form_' . $formId,
                'status'        => 'new',
            ];

            // Map common fields
            $fieldMappings = [
                'email'      => ['email', 'contact_email', 'user_email'],
                'first_name' => ['first_name', 'fname', 'firstname'],
                'last_name'  => ['last_name', 'lname', 'lastname'],
                'company'    => ['company', 'company_name', 'organization'],
                'phone'      => ['phone', 'telephone', 'phone_number'],
                'message'    => ['message', 'comments', 'notes', 'description'],
            ];

            foreach ($fieldMappings as $leadField => $possibleFormFields) {
                foreach ($possibleFormFields as $formField) {
                    if (!empty($submissionData[$formField] ?? null)) {
                        $leadData[$leadField] = (string) $submissionData[$formField];
                        break;
                    }
                }
            }

            // Create lead
            $stmt = $db->prepare("
                INSERT INTO leads 
                    (tenant_id, form_id, submission_id, first_name, last_name, email, phone, company, source, notes, status, created_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
            ");

            $stmt->execute([
                $leadData['tenant_id'],
                $leadData['form_id'],
                $leadData['submission_id'],
                $leadData['first_name'] ?? null,
                $leadData['last_name']  ?? null,
                $leadData['email']      ?? null,
                $leadData['phone']      ?? null,
                $leadData['company']    ?? null,
                $leadData['source'],
                $leadData['message']    ?? 'Form submission',
                $leadData['status'],
            ]);

            $leadId = (int) $db->lastInsertId();

            // Update submission with lead ID
            $stmt = $db->prepare("UPDATE form_submissions SET lead_id = ? WHERE id = ?");
            $stmt->execute([$leadId, $submissionId]);

            // Auto-score lead with AI if enabled
            if (!empty($settings['auto_ai_scoring'])) {
                $this->autoScoreLead($leadId, $submissionData);
            }

            return $leadId;
        } catch (Exception $e) {
            error_log('Lead creation from form submission failed: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Call AIService to score the lead & update DB
     */
    private function autoScoreLead(int $leadId, array $submissionData): void
    {
        try {
            $aiService = new AIService();
            $lead      = $this->getLeadForScoring($leadId, $submissionData);
            $aiScore   = $aiService->scoreLead($lead);

            $db  = Database::getConnection();
            $stmt = $db->prepare("
                UPDATE leads 
                   SET ai_score = ?, 
                       ai_score_category = ?, 
                       ai_score_reasoning = ?,
                       ai_confidence = ?, 
                       ai_model_used = ?, 
                       last_ai_scoring = NOW()
                 WHERE id = ?
            ");
            $stmt->execute([
                $aiScore['score']      ?? null,
                $aiScore['category']   ?? null,
                $aiScore['reasoning']  ?? null,
                $aiScore['confidence'] ?? null,
                $aiScore['ai_model']   ?? null,
                $leadId,
            ]);
        } catch (Exception $e) {
            error_log('Auto AI scoring failed: ' . $e->getMessage());
        }
    }

    /**
     * Fetch the lead record & merge submission data for AI scoring
     */
    private function getLeadForScoring(int $leadId, array $submissionData): array
    {
        $db = Database::getConnection();

        $stmt = $db->prepare("SELECT * FROM leads WHERE id = ?");
        $stmt->execute([$leadId]);
        $lead = (array) $stmt->fetch(PDO::FETCH_ASSOC);

        // Enhance lead data with submission payload
        $lead['submission_data'] = $submissionData;
        $lead['notes']           = 'Form submission: ' . json_encode($submissionData, JSON_UNESCAPED_UNICODE);

        return $lead;
    }
}
