<?php
require_once __DIR__ . '/../Core.php';
require_once __DIR__ . '/../services.php';
require_once __DIR__ . '/../models/Contact.php';
require_once __DIR__ . '/../models/Segment.php';
require_once __DIR__ . '/../models/Suppression.php';
require_once __DIR__ . '/../models/CustomField.php';
require_once __DIR__ . '/../models/ConsentLog.php';
require_once __DIR__ . '/../models/ContactTask.php';
require_once __DIR__ . '/../models/ContactNote.php';
require_once __DIR__ . '/../models/User.php';
require_once __DIR__ . '/../models/Account.php';
require_once __DIR__ . '/../models/Pipeline.php';
require_once __DIR__ . '/../models/Reminder.php';
require_once __DIR__ . '/../models/ContactList.php';

class ContactsController extends Controller
{
    public function index(): void
    {
        require_permission('contacts.view');
        $q = trim($_GET['q'] ?? '');
        $channel = $_GET['channel'] ?? 'all';
        $segmentId = (int)($_GET['segment_id'] ?? 0) ?: null;
        $page = max(1, (int)($_GET['page'] ?? 1));
        $perPage = resolve_list_per_page('per_page', 15);
        $result = (new Contact())->search($q, $channel, $page, $perPage, $segmentId);
        $segmentModel = new Segment();
        $segments = $segmentModel->all();
        foreach ($segments as &$segment) {
            $segment['member_count'] = $segmentModel->countMembers((int)$segment['id']);
        }
        $this->view('contacts/index', [
            'contacts' => $result['data'],
            'meta' => $result['meta'],
            'filters' => ['q' => $q, 'channel' => $channel, 'segment_id' => $segmentId, 'per_page' => $perPage],
            'segments' => $segments,
            'customFields' => (new CustomField())->all(),
            'users' => (new User())->all(),
            'accounts' => (new Account())->all(),
            'pipelines' => (new Pipeline())->all(),
            'contactLists' => (new ContactList())->all(),
            'contactCounts' => (new Contact())->counts(),
            'perPageOptions' => list_per_page_options(),
            'perPage' => $perPage,
        ]);
    }

    public function show(): void
    {
        require_permission('contacts.view');
        $id = (int)($_GET['id'] ?? 0);
        $contactModel = new Contact();
        $contact = $contactModel->find($id);
        if (!$contact) {
            http_response_code(404);
            exit('Contact not found.');
        }

        $suppressionModel = new Suppression();
        $suppressed = [
            'sms' => !empty($contact['phone']) ? $suppressionModel->isSuppressed('sms', (string)$contact['phone']) : false,
            'email' => !empty($contact['email']) ? $suppressionModel->isSuppressed('email', (string)$contact['email']) : false,
        ];
        $customFieldModel = new CustomField();
        $activeCommTab = ($_GET['comm'] ?? 'sms') === 'email' ? 'email' : 'sms';
        $emailThreads = $contactModel->communicationThreads($id, 'email', (string)($contact['email'] ?? ''), 250);
        $smsThreads = $contactModel->communicationThreads($id, 'sms', (string)($contact['phone'] ?? ''), 250);
        $selectedEmailThreadKey = trim((string)($_GET['email_thread'] ?? ($emailThreads[0]['conversation_key'] ?? '')));
        $selectedSmsThreadKey = trim((string)($_GET['sms_thread'] ?? ($smsThreads[0]['conversation_key'] ?? '')));
        $selectedEmailThread = null;
        foreach ($emailThreads as $thread) {
            if (($thread['conversation_key'] ?? '') === $selectedEmailThreadKey) {
                $selectedEmailThread = $thread;
                break;
            }
        }
        $selectedSmsThread = null;
        foreach ($smsThreads as $thread) {
            if (($thread['conversation_key'] ?? '') === $selectedSmsThreadKey) {
                $selectedSmsThread = $thread;
                break;
            }
        }

        $this->view('contacts/show', [
            'contact' => $contact,
            'summary' => $contactModel->engagementSummary($id),
            'timeline' => $contactModel->activityTimeline($id),
            'suppressed' => $suppressed,
            'suppressionEntries' => $suppressionModel->forContact($id),
            'customFields' => $customFieldModel->all(),
            'customValues' => $customFieldModel->valuesForContact($id),
            'consentLogs' => (new ConsentLog())->forContact($id),
            'tasks' => (new ContactTask())->forContact($id),
            'notes' => (new ContactNote())->forContact($id),
            'reminders' => (new Reminder())->forContact($id),
            'users' => (new User())->all(),
            'accounts' => (new Account())->all(),
            'pipelines' => (new Pipeline())->all(),
            'contactLists' => (new ContactList())->all(),
            'contactCounts' => (new Contact())->counts(),
            'perPageOptions' => list_per_page_options(),
            'perPage' => $perPage,
            'activeCommTab' => $activeCommTab,
            'emailThreads' => $emailThreads,
            'smsThreads' => $smsThreads,
            'selectedEmailThread' => $selectedEmailThread,
            'selectedSmsThread' => $selectedSmsThread,
        ]);
    }

    public function save(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        try {
            $contactModel = new Contact();
            $listId = (int)($_POST['add_to_list_id'] ?? 0);
            if ($listId > 0) {
                $list = (new ContactList())->find($listId);
                if (!$list) {
                    throw new RuntimeException('The selected list was not found.');
                }
            }

            $result = $contactModel->createOrReuse($_POST);
            $contactId = (int)$result['id'];
            $matchedBy = $result['matched_by'] ? strtoupper((string)$result['matched_by']) : 'EMAIL/PHONE';

            if ($listId > 0) {
                (new ContactList())->addMember($listId, $contactId);
                flash('success', $result['created']
                    ? 'Contact saved and added to the selected list.'
                    : 'Existing contact matched by ' . $matchedBy . '; no duplicate was created. The contact was refreshed and added to the selected list.');
            } else {
                flash('success', $result['created']
                    ? 'Contact saved successfully.'
                    : 'Existing contact matched by ' . $matchedBy . '; no duplicate was created. Missing details were refreshed.');
            }
        } catch (Throwable $e) {
            flash('error', $e->getMessage());
        }
        redirect('contacts/index');
    }

    public function import(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        try {
            $file = upload_file($_FILES['csv_file'] ?? [], ['csv']);
            if (!$file) {
                throw new RuntimeException('Please upload a CSV file.');
            }
            $stats = (new Contact())->importCsv(uploaded_file_path($file));
            flash('success', 'Processed ' . (int)$stats['processed'] . ' contacts: ' . (int)$stats['new'] . ' new, ' . (int)$stats['existing'] . ' existing reused, ' . (int)$stats['skipped'] . ' skipped.');
        } catch (Throwable $e) {
            flash('error', $e->getMessage());
        }
        redirect('contacts/index');
    }

    public function exportCsv(): void
    {
        require_permission('messages.export');
        $q = trim($_GET['q'] ?? '');
        $channel = $_GET['channel'] ?? 'all';
        $segmentId = (int)($_GET['segment_id'] ?? 0) ?: null;
        $rows = (new Contact())->exportRows($q, $channel, $segmentId);
        header('Content-Type: text/csv');
        header('Content-Disposition: attachment; filename="contacts_export.csv"');
        $out = fopen('php://output', 'w');
        fputcsv($out, ['ID','First Name','Last Name','Phone','Email','Company','Tags','Timezone','Lifecycle Stage','Score','Unsubscribed SMS','Unsubscribed Email']);
        foreach ($rows as $row) {
            fputcsv($out, [$row['id'], $row['first_name'], $row['last_name'], $row['phone'], $row['email'], $row['company'], $row['tags'], $row['timezone'] ?? '', $row['lifecycle_stage'] ?? '', $row['score'] ?? 0, $row['unsubscribed_sms'], $row['unsubscribed_email']]);
        }
        fclose($out);
        exit;
    }

    public function delete(): void
    {
        require_permission('contacts.delete');
        $id = (int)($_GET['id'] ?? 0);
        if ($id) {
            (new Contact())->delete($id);
            flash('success', 'Contact deleted.');
        }
        redirect('contacts/index');
    }

    public function saveSegment(): void
    {
        require_permission('segments.manage');
        verify_csrf();
        $id = (new Segment())->create($_POST);
        audit_log('segment.created', 'segment', $id, ['name' => $_POST['name'] ?? null]);
        flash('success', 'Segment saved.');
        redirect('contacts/index');
    }

    public function deleteSegment(): void
    {
        require_permission('segments.manage');
        $id = (int)($_GET['id'] ?? 0);
        if ($id) {
            (new Segment())->delete($id);
            audit_log('segment.deleted', 'segment', $id);
            flash('success', 'Segment deleted.');
        }
        redirect('contacts/index');
    }

    public function saveContactList(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $id = (new ContactList())->create([
            'name' => $_POST['name'] ?? '',
            'channel' => $_POST['channel'] ?? 'both',
            'description' => $_POST['description'] ?? '',
            'created_by' => current_user()['id'] ?? null,
        ]);
        audit_log('contact_list.created', 'contact_list', $id, ['name' => $_POST['name'] ?? '']);
        flash('success', 'Contact list saved.');
        redirect('contacts/index');
    }

    public function importContactList(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $listId = (int)($_POST['list_id'] ?? 0);
        try {
            $file = upload_file($_FILES['csv_file'] ?? [], ['csv']);
            if (!$file) {
                throw new RuntimeException('Please upload a CSV file.');
            }
            $stats = (new ContactList())->importCsv($listId, uploaded_file_path($file));
            audit_log('contact_list.imported', 'contact_list', $listId, $stats);
            flash('success', 'Processed ' . (int)$stats['processed'] . ' list rows: ' . (int)$stats['new'] . ' new contacts, ' . (int)$stats['existing'] . ' existing reused, ' . (int)$stats['skipped'] . ' skipped.');
        } catch (Throwable $e) {
            flash('error', $e->getMessage());
        }
        redirect('contacts/index');
    }

    public function deleteContactList(): void
    {
        require_permission('contacts.manage');
        $id = (int)($_GET['id'] ?? 0);
        if ($id) {
            (new ContactList())->delete($id);
            audit_log('contact_list.deleted', 'contact_list', $id);
            flash('success', 'Contact list deleted.');
        }
        redirect('contacts/index');
    }

    public function saveCustomValues(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        (new CustomField())->saveValuesForContact($contactId, $_POST['custom'] ?? []);
        audit_log('contact.custom_fields.updated', 'contact', $contactId);
        flash('success', 'Custom field values saved.');
        $this->redirectToShow($contactId, (string)($_POST['comm'] ?? ''));
    }

    public function addConsent(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        $channel = ($_POST['channel'] ?? 'email') === 'sms' ? 'sms' : 'email';
        $status = ($_POST['status'] ?? 'opt_in') === 'opt_out' ? 'opt_out' : 'opt_in';
        $this->recordConsent(
            $contactId,
            $channel,
            $status,
            trim((string)($_POST['source'] ?? 'manual')) ?: 'manual',
            trim((string)($_POST['proof_note'] ?? ''))
        );
        $contact = (new Contact())->find($contactId);
        if ($contact) {
            $address = $channel === 'sms' ? (string)($contact['phone'] ?? '') : (string)($contact['email'] ?? '');
            if ($address !== '') {
                (new Contact())->setUnsubscribed($channel, $address, $status !== 'opt_in');
                if ($status !== 'opt_in') {
                    (new Suppression())->add($channel, $address, 'manual_unsubscribe', $contactId, null);
                } else {
                    (new Suppression())->removeByChannelAddress($channel, $address);
                }
            }
        }
        audit_log('contact.consent.logged', 'contact', $contactId, ['channel' => $channel, 'status' => $status]);
        flash('success', 'Consent record added.');
        $this->redirectToShow($contactId, $channel);
    }

    public function updateProfile(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        if ($contactId > 0) {
            (new Contact())->updateLifecycleProfile($contactId, [
                'lifecycle_stage' => $_POST['lifecycle_stage'] ?? 'lead',
                'score' => (int)($_POST['score'] ?? 0),
                'owner_user_id' => (int)($_POST['owner_user_id'] ?? 0) ?: null,
                'account_id' => (int)($_POST['account_id'] ?? 0) ?: null,
                'pipeline_id' => (int)($_POST['pipeline_id'] ?? 0) ?: null,
            ]);
            audit_log('contact.profile.updated', 'contact', $contactId, [
                'lifecycle_stage' => $_POST['lifecycle_stage'] ?? 'lead',
                'score' => (int)($_POST['score'] ?? 0),
                'account_id' => (int)($_POST['account_id'] ?? 0),
                'pipeline_id' => (int)($_POST['pipeline_id'] ?? 0),
            ]);
            flash('success', 'Contact lifecycle profile updated.');
        }
        $this->redirectToShow($contactId, (string)($_POST['comm'] ?? ''));
    }

    public function updateSubscription(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        $channel = ($_POST['channel'] ?? 'email') === 'sms' ? 'sms' : 'email';
        $action = ($_POST['subscription_action'] ?? 'unsubscribe') === 'resubscribe' ? 'resubscribe' : 'unsubscribe';
        $contact = (new Contact())->find($contactId);
        if (!$contact) {
            flash('error', 'Contact not found.');
            redirect('contacts/index');
        }

        $address = $channel === 'sms' ? trim((string)($contact['phone'] ?? '')) : trim((string)($contact['email'] ?? ''));
        if ($address === '') {
            flash('error', 'This contact does not have a ' . strtoupper($channel) . ' address.');
            $this->redirectToShow($contactId, $channel);
        }

        $suppressionModel = new Suppression();
        if ($action === 'unsubscribe') {
            $suppressionModel->add($channel, $address, trim((string)($_POST['reason'] ?? 'manual_unsubscribe')) ?: 'manual_unsubscribe', $contactId, null);
            (new Contact())->setUnsubscribed($channel, $address, true);
            $this->recordConsent($contactId, $channel, 'opt_out', 'contact_profile', trim((string)($_POST['proof_note'] ?? 'Manual unsubscribe from contact profile')) ?: 'Manual unsubscribe from contact profile');
            audit_log('contact.unsubscribed', 'contact', $contactId, ['channel' => $channel]);
            flash('success', strtoupper($channel) . ' was unsubscribed and added to the unsubscribe list.');
        } else {
            $suppressionModel->removeByChannelAddress($channel, $address);
            (new Contact())->setUnsubscribed($channel, $address, false);
            $this->recordConsent($contactId, $channel, 'opt_in', 'contact_profile', trim((string)($_POST['proof_note'] ?? 'Manual resubscribe from contact profile')) ?: 'Manual resubscribe from contact profile');
            audit_log('contact.resubscribed', 'contact', $contactId, ['channel' => $channel]);
            flash('success', strtoupper($channel) . ' was restored and removed from the unsubscribe list.');
        }

        $this->redirectToShow($contactId, $channel);
    }

    public function sendSmsMessage(): void
    {
        require_permission('messages.send');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        $contact = $contactId ? (new Contact())->find($contactId) : null;
        if (!$contact) {
            flash('error', 'Contact not found.');
            redirect('contacts/index');
        }
        $to = trim((string)($contact['phone'] ?? ''));
        $body = apply_sms_preferences($_POST['body'] ?? '', (int)(current_user()['id'] ?? 0));
        $mediaUrl = trim((string)($_POST['media_url'] ?? '')) ?: null;
        try {
            if ($to === '') {
                throw new RuntimeException('This contact does not have a phone number.');
            }
            if ((new Suppression())->isSuppressed('sms', $to)) {
                throw new RuntimeException('This SMS/MMS recipient is on the suppression list.');
            }
            $timezone = $contact['timezone'] ?? quiet_hours_settings()['timezone'];
            if (is_within_quiet_hours($timezone)) {
                throw new RuntimeException(quiet_hours_notice($timezone));
            }
            $result = (new TwilioService())->sendSms($to, $body, $mediaUrl);
            $messageId = (new Message())->create([
                'contact_id' => $contactId,
                'channel' => 'sms',
                'direction' => 'outbound',
                'body' => $body,
                'from_address' => effective_twilio_config((int)(current_user()['id'] ?? 0))['from_number'],
                'to_address' => $to,
                'status' => $result['status'] ?? 'sent',
                'external_id' => $result['sid'] ?? null,
                'media_url' => $mediaUrl,
                'metadata_json' => json_encode($result),
            ]);
            audit_log('contact.sms.sent', 'message', $messageId, ['contact_id' => $contactId]);
            flash('success', 'SMS/MMS sent successfully.');
        } catch (Throwable $e) {
            flash('error', $e->getMessage());
        }
        $this->redirectToShow($contactId, 'sms');
    }

    public function sendEmailMessage(): void
    {
        require_permission('messages.send');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        $contact = $contactId ? (new Contact())->find($contactId) : null;
        if (!$contact) {
            flash('error', 'Contact not found.');
            redirect('contacts/index');
        }
        $to = trim((string)($contact['email'] ?? ''));
        $subject = trim((string)($_POST['subject'] ?? ''));
        $body = apply_email_preferences($_POST['body'] ?? '', (int)(current_user()['id'] ?? 0));
        $attachments = [];
        $saved = null;
        $messageId = null;
        try {
            if ($to === '') {
                throw new RuntimeException('This contact does not have an email address.');
            }
            if ((new Suppression())->isSuppressed('email', $to)) {
                throw new RuntimeException('This email recipient is on the suppression list.');
            }
            $timezone = $contact['timezone'] ?? quiet_hours_settings()['timezone'];
            if (is_within_quiet_hours($timezone)) {
                throw new RuntimeException(quiet_hours_notice($timezone));
            }
            if (!empty($_FILES['attachment']['name'])) {
                $saved = upload_file($_FILES['attachment'], ['pdf', 'doc', 'docx', 'jpg', 'jpeg', 'png', 'gif']);
                if ($saved) {
                    $attachments[] = uploaded_file_path($saved);
                }
            }
            $mailConfig = effective_mail_config((int)(current_user()['id'] ?? 0));
            $messageModel = new Message();
            $messageId = $messageModel->create([
                'contact_id' => $contactId,
                'channel' => 'email',
                'direction' => 'outbound',
                'subject' => $subject,
                'body' => $body,
                'from_address' => $mailConfig['from_email'],
                'to_address' => $to,
                'status' => 'queued',
                'attachment_path' => $saved,
            ]);
            $trackedHtml = instrument_email_html($body, $messageId);
            $externalId = '<msg-' . $messageId . '@' . parse_url(config('app.base_url'), PHP_URL_HOST) . '>';
            $ok = (new EmailService())->sendEmail($to, $subject, $trackedHtml, strip_tags($body), $attachments, ['Message-ID: ' . $externalId, 'Reply-To: ' . $mailConfig['reply_to_email']]);
            if (!$ok) {
                throw new RuntimeException('Email send failed. Check SendGrid, SMTP, or server mail settings.');
            }
            $messageModel->updateExternalAndStatus($messageId, $externalId, 'sent', null);
            audit_log('contact.email.sent', 'message', $messageId, ['contact_id' => $contactId]);
            flash('success', 'Email sent successfully.');
        } catch (Throwable $e) {
            if (!empty($messageId)) {
                (new Message())->markFailedIfPending((int)$messageId, json_encode(['error' => $e->getMessage(), 'failed_at' => date('c')]));
            }
            flash('error', $e->getMessage());
        }
        $this->redirectToShow($contactId, 'email');
    }

    public function addTask(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        $taskId = (new ContactTask())->create($_POST);
        audit_log('contact.task.created', 'contact_task', $taskId, ['contact_id' => $contactId]);
        flash('success', 'Task added.');
        $this->redirectToShow($contactId, (string)($_POST['comm'] ?? ''));
    }

    public function completeTask(): void
    {
        require_permission('contacts.manage');
        $taskId = (int)($_GET['task_id'] ?? 0);
        $contactId = (int)($_GET['contact_id'] ?? 0);
        if ($taskId > 0) {
            (new ContactTask())->markDone($taskId);
            audit_log('contact.task.completed', 'contact_task', $taskId, ['contact_id' => $contactId]);
            flash('success', 'Task marked done.');
        }
        $this->redirectToShow($contactId, (string)($_GET['comm'] ?? ''));
    }

    public function addNote(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        $noteId = (new ContactNote())->create($_POST);
        audit_log('contact.note.created', 'contact_note', $noteId, ['contact_id' => $contactId]);
        flash('success', 'Note added.');
        $this->redirectToShow($contactId, (string)($_POST['comm'] ?? ''));
    }

    public function addReminder(): void
    {
        require_permission('contacts.manage');
        verify_csrf();
        $contactId = (int)($_POST['contact_id'] ?? 0);
        $id = (new Reminder())->create($_POST);
        audit_log('contact.reminder.created', 'reminder', $id, ['contact_id' => $contactId]);
        flash('success', 'Reminder added.');
        $this->redirectToShow($contactId, (string)($_POST['comm'] ?? ''));
    }

    public function completeReminder(): void
    {
        require_permission('contacts.manage');
        $contactId = (int)($_GET['contact_id'] ?? 0);
        $reminderId = (int)($_GET['reminder_id'] ?? 0);
        if ($reminderId) {
            (new Reminder())->markDone($reminderId);
            audit_log('contact.reminder.completed', 'reminder', $reminderId, ['contact_id' => $contactId]);
            flash('success', 'Reminder completed.');
        }
        $this->redirectToShow($contactId, (string)($_GET['comm'] ?? ''));
    }

    private function recordConsent(int $contactId, string $channel, string $status, string $source, string $proofNote): void
    {
        (new ConsentLog())->create([
            'contact_id' => $contactId,
            'channel' => $channel,
            'status' => $status,
            'source' => $source,
            'proof_note' => $proofNote,
            'ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
            'created_by' => current_user()['id'] ?? null,
        ]);
    }

    private function redirectToShow(int $contactId, string $comm = ''): void
    {
        $url = 'index.php?route=contacts/show&id=' . $contactId;
        if ($comm !== '') {
            $url .= '&comm=' . urlencode($comm);
        }
        redirect_url($url);
    }
}
