<?php

namespace App\Console\Commands;

use App\Enum\Contact\MessageChannel;
use App\Enum\Contact\MessageDirection;
use App\Enum\Contact\MessageProvider;
use App\Enum\Contact\MessageStatus;
use App\Jobs\Synapse\ClassifyInboundEmailJob;
use App\Models\Contact\Hub\ContactMessage;
use App\Models\Contact\Hub\ContactMessageAttachment;
use App\Models\Solicitor\SolicitorEmailSender;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;

class FakeInboundEmail extends Command
{
    protected $signature = 'inbox:fake
                            {--to=       : Inbox address (SolicitorEmailSender email); omit to pick interactively}
                            {--from=     : Sender email address}
                            {--subject=  : Email subject line}
                            {--body=     : Plain-text body}
                            {--count=1   : Number of emails to create}
                            {--seed      : Use realistic randomised content instead of prompts}
                            {--spread    : When seeding, distribute emails across ALL inboxes}
                            {--classify  : Dispatch classification job for each created email}';

    protected $description = 'Inject fake inbound email(s) into the inbox for local testing';

    // ── Realistic seed data ───────────────────────────────────────────────────

    private array $senderNames = [
        'NHS Lothian Records Team', 'County Durham & Darlington NHS FT',
        'Bupa UK Information Governance', 'Care UK Data Requests',
        'HC-One Subject Access Team', 'Four Seasons Health Care',
        'Priory Group Information Rights', 'Barchester Healthcare SAR Team',
        'NHS Greater Manchester ICB', 'Tameside & Glossop Integrated Care',
        'Doncaster & Bassetlaw Teaching Hospitals', 'Blackpool Teaching Hospitals',
        'Mid Cheshire Hospitals NHS FT', 'University Hospitals Coventry',
    ];

    private array $senderDomains = [
        'nhslothian.scot.nhs.uk', 'cddft.nhs.uk', 'bupa.com',
        'careuk.com', 'hc-one.co.uk', 'fshc.co.uk',
        'priorygroup.com', 'barchester.com', 'greatermanchester.nhs.uk',
        'tamesideglossop.nhs.uk', 'dbth.nhs.uk', 'bfwh.nhs.uk',
        'mcht.nhs.uk', 'uhcw.nhs.uk',
    ];

    private array $subjects = [
        'Re: Subject Access Request – Response Enclosed',
        'DSAR Response – Records Released',
        'Data Subject Access Request – Partial Disclosure',
        'Response to Your Subject Access Request',
        'SAR Acknowledgement – Processing Underway',
        'Subject Access Request – Further Information Required',
        'Data Request Response – Records Attached',
        'DSAR – Extension Notice (30 Days)',
        'Re: Request for Medical Records',
        'Subject Access Request – Unable to Locate Records',
        'SAR Response – Third Party Redaction Applied',
        'Data Subject Request – Clarification Needed',
        'Re: Outstanding Subject Access Request',
        'DSAR – Final Response Letter',
    ];

    private array $bodies = [
        "Dear Sir/Madam,\n\nThank you for your subject access request received on [DATE]. Please find enclosed the records held by this organisation relating to your client.\n\nIf you require any further information, please do not hesitate to contact us.\n\nKind regards,\nInformation Governance Team",
        "Dear Solicitor,\n\nWe write in response to your data subject access request. After conducting a thorough search of our records, we have been unable to locate any records matching the details provided.\n\nIf you believe this is an error, please provide additional identifying information.\n\nYours faithfully,\nRecords Department",
        "Dear Sir/Madam,\n\nWe acknowledge receipt of your subject access request dated [DATE].\n\nPlease be aware that due to the volume of requests currently being processed, we require an extension of 30 days in accordance with Article 12(3) of the UK GDPR.\n\nWe anticipate responding by [DATE+60].\n\nRegards,\nData Protection Officer",
        "Dear Solicitor,\n\nPlease find attached our response to the subject access request submitted on behalf of your client.\n\nPlease note that certain third-party information has been redacted in accordance with our obligations under the Data Protection Act 2018.\n\nKind regards,\nSubject Access Team",
        "Dear Sir/Madam,\n\nWe are writing to advise that in order to process your subject access request, we require further information to verify the identity of the data subject.\n\nPlease provide a copy of photo ID and proof of address at your earliest convenience.\n\nYours sincerely,\nInformation Governance",
        "To Whom It May Concern,\n\nWe have received your subject access request and are currently processing it. We will respond within the statutory one-month period.\n\nShould you have any queries in the meantime, please contact our Information Governance team.\n\nKind regards,\nRecords & Data Management",
    ];

    /**
     * Pairs of [filename, letter-body] used as fake PDF attachments in seed mode.
     * Content is intentionally verbose so the AI agents have something substantive to read.
     */
    private array $attachmentTemplates = [
        [
            'filename' => 'DSAR-Response-Letter.pdf',
            'content'  => "DATA SUBJECT ACCESS REQUEST — FORMAL RESPONSE\n\nDear Solicitor,\n\nWe write in response to the Subject Access Request (SAR) submitted on behalf of your client. We have conducted a thorough search of our records systems and are pleased to confirm that we have located the following information held in relation to the data subject:\n\n- Admission records dated between 1987 and 1992\n- Nursing care notes and daily observation logs\n- Medication administration records\n- Correspondence files\n\nPlease find the above records enclosed with this letter. All third-party information has been redacted in accordance with Section 40 of the Data Protection Act 2018. Where records have been withheld in part, the relevant exemption is noted against each entry.\n\nShould you require clarification on any aspect of this disclosure, please contact our Information Governance department quoting reference IG/SAR/2024/0847.\n\nYours sincerely,\nInformation Governance Team\nData Protection Officer",
        ],
        [
            'filename' => 'SAR-No-Records-Found.pdf',
            'content'  => "SUBJECT ACCESS REQUEST — OUTCOME NOTIFICATION\n\nDear Sir/Madam,\n\nThank you for your Subject Access Request received on behalf of your client.\n\nWe write to advise that, following a thorough and comprehensive search of all our records systems — including archived paper records, electronic patient records, and correspondence databases — we have been unable to locate any records held in relation to the individual named in your request.\n\nOur search covered the following systems:\n- Electronic Patient Record (EPR)\n- Archived paper records (pre-2005)\n- Legacy care management database\n- General correspondence files\n\nIf you believe records may exist that we have not located, please provide further identifying information such as a previous address, date of birth, or alternative name under which records may have been held.\n\nWe are satisfied that we have fulfilled our obligations under Article 15 of the UK GDPR.\n\nYours faithfully,\nRecords & Information Governance\nData Protection Officer",
        ],
        [
            'filename' => 'Extension-Notice-DSAR.pdf',
            'content'  => "SUBJECT ACCESS REQUEST — EXTENSION OF TIME NOTICE\n\nDear Solicitor,\n\nWe acknowledge receipt of your Subject Access Request dated as per our records.\n\nPursuant to Article 12(3) of the UK GDPR, we wish to advise that due to the complexity and volume of records to be reviewed in relation to this request, we require an extension of the standard one-month response period.\n\nWe are therefore notifying you that we will provide our full response within three months of the date of receipt of your original request.\n\nReasons for the extension include:\n- Large volume of archived paper records requiring manual review\n- Records held across multiple sites\n- Need to consult with clinical staff regarding third-party redactions\n\nWe apologise for any inconvenience this may cause and remain committed to fulfilling our obligations under data protection legislation.\n\nYours sincerely,\nInformation Governance Department",
        ],
        [
            'filename' => 'Wrong-Organisation-Response.pdf',
            'content'  => "SUBJECT ACCESS REQUEST — RESPONSE REGARDING INCORRECT RECIPIENT\n\nDear Sir/Madam,\n\nWe have received a Subject Access Request which appears to have been directed to this organisation in error.\n\nHaving reviewed the details provided, we can confirm that this organisation — [Trust Name] — does not hold, and has not held, records in relation to the care setting described in your request. The care home / residential facility referenced in your correspondence was operated by a separate organisation and is not part of our NHS Trust.\n\nWe would recommend directing your request to the appropriate data controller. Based on the information you have provided, you may wish to contact:\n\n- The local authority adult social care department for the relevant period\n- The Care Quality Commission for information regarding the registered provider\n- NHS England for records held centrally\n\nWe trust this assists you in directing your request appropriately.\n\nYours faithfully,\nRecords Management\nInformation Governance Team",
        ],
        [
            'filename' => 'DSAR-Partial-Disclosure.pdf',
            'content'  => "SUBJECT ACCESS REQUEST — PARTIAL DISCLOSURE LETTER\n\nDear Solicitor,\n\nPlease find enclosed our response to the Subject Access Request submitted on behalf of your client.\n\nFollowing a thorough review of our records, we have located the documents set out in the accompanying schedule. However, we wish to draw your attention to the following limitations of this disclosure:\n\n1. RECORDS DESTRUCTION: Records relating to the period prior to 1985 have been destroyed in accordance with our records retention policy, which was compliant with NHS guidance at the time. We are unable to confirm whether records from this period existed.\n\n2. THIRD-PARTY REDACTIONS: Information relating to third parties (other service users, staff, and relatives) has been redacted pursuant to Section 40(2) of the DPA 2018. Redacted sections are marked in the enclosed documentation.\n\n3. CLINICAL OPINION WITHHELD: Certain clinical opinion notes have been withheld as they relate to third-party clinical professionals who have not consented to disclosure.\n\nA schedule of all documents reviewed and the basis for any withholding is enclosed.\n\nYours sincerely,\nData Protection & Information Governance\nSubject Access Team",
        ],
        [
            'filename' => 'DSAR-Rejection-Letter.pdf',
            'content'  => "SUBJECT ACCESS REQUEST — REFUSAL NOTICE\n\nDear Sir/Madam,\n\nWe write in response to the Subject Access Request submitted on behalf of your client.\n\nHaving carefully considered the request, we are refusing to comply on the following grounds:\n\nGROUNDS FOR REFUSAL:\nThe request is considered manifestly unfounded pursuant to Article 12(5) of the UK GDPR. We have received multiple substantially similar requests from this firm relating to the same data subject within a short period of time. We are satisfied that the requests are being made for reasons other than the exercise of the rights conferred by the UK GDPR.\n\nAlternatively / in addition, the request is considered manifestly excessive given the breadth of information requested and the administrative burden it would impose on this organisation.\n\nYOUR RIGHTS:\nIf you disagree with this decision, you may complain to the Information Commissioner's Office (ICO) at https://ico.org.uk/. You also have the right to seek judicial remedy.\n\nYours faithfully,\nData Protection Officer",
        ],
    ];

    // ─────────────────────────────────────────────────────────────────────────

    public function handle(): int
    {
        $count  = max(1, (int) $this->option('count'));
        $seed   = $this->option('seed');
        $spread = $this->option('spread');

        // ── Load available senders ─────────────────────────────────────────
        $allSenders = SolicitorEmailSender::with('solicitor')->orderBy('email_address')->get();

        if ($allSenders->isEmpty()) {
            $this->error('No sender email addresses are configured. Add one in the admin panel first.');
            return self::FAILURE;
        }

        // ── Resolve target sender(s) ───────────────────────────────────────
        if ($spread && $seed) {
            $targetSenders = $allSenders;
            $this->line("  <info>→</info> Spreading across <comment>{$allSenders->count()}</comment> inboxes");
        } else {
            $targetSenders = new Collection([$this->resolveSingleSender($allSenders)]);
            if (!$targetSenders->first()) {
                return self::FAILURE;
            }
        }

        // ── Single email (interactive) ─────────────────────────────────────
        if ($count === 1 && !$seed) {
            $sender  = $targetSenders->first();
            $from    = $this->option('from')    ?: $this->ask('From address', 'test@example.com');
            $subject = $this->option('subject') ?: $this->ask('Subject', 'Test inbound email');
            $body    = $this->option('body')    ?: $this->ask('Body (plain text)', 'This is a test inbound email.');

            $msg = $this->createMessage($sender, $from, $subject, $body);

            if ($this->option('classify')) {
                ClassifyInboundEmailJob::dispatch($msg->id);
            }

            $this->info("✓ Inbound email created (ID #{$msg->id})");
            $this->table(['Field', 'Value'], [
                ['ID',        $msg->id],
                ['To',        $sender->email_address],
                ['From',      $msg->from_email],
                ['Subject',   $msg->subject],
                ['Classify',  $this->option('classify') ? 'queued' : 'skipped'],
                ['URL',       url('/inbox')],
            ]);

            return self::SUCCESS;
        }

        // ── Bulk / seed mode ───────────────────────────────────────────────
        $faker   = \Faker\Factory::create('en_GB');
        $created = [];

        $this->withProgressBar(range(1, $count), function () use (
            $faker, $targetSenders, $seed, &$created
        ) {
            $sender = $targetSenders->random();

            if ($seed) {
                $nameIndex    = array_rand($this->senderNames);
                $senderName   = $this->senderNames[$nameIndex];
                $senderDomain = $this->senderDomains[$nameIndex];
                $localPart    = strtolower(str_replace(' ', '.', preg_replace('/[^a-zA-Z ]/', '', $senderName)));
                $from         = "{$senderName} <{$localPart}@{$senderDomain}>";
                $subject      = $this->subjects[array_rand($this->subjects)];
                $body         = $this->bodies[array_rand($this->bodies)];
                $readAt       = $faker->boolean(40) ? $faker->dateTimeBetween('-14 days', 'now') : null;
                $createdAt    = $faker->dateTimeBetween('-30 days', 'now');
            } else {
                $from      = $this->option('from')    ?: "test{$faker->randomNumber(3)}@example.com";
                $subject   = $this->option('subject') ?: "Test email #{$faker->randomNumber(4)}";
                $body      = $this->option('body')    ?: $faker->paragraph(3);
                $readAt    = null;
                $createdAt = now();
            }

            $msg = $this->createMessage($sender, $from, $subject, $body, $readAt);

            if (isset($createdAt)) {
                $msg->timestamps = false;
                $msg->forceFill(['created_at' => $createdAt, 'updated_at' => $createdAt])->save();
                $msg->timestamps = true;
            }

            // ~55% of seeded emails get 1-2 realistic PDF attachments
            if ($seed && $faker->boolean(55)) {
                $attachCount = $faker->numberBetween(1, 2);
                $templates   = (array) $faker->randomElements($this->attachmentTemplates, $attachCount);
                foreach ($templates as $tpl) {
                    $this->createFakeAttachment($msg, $tpl['filename'], $tpl['content']);
                }
            }

            if ($this->option('classify')) {
                ClassifyInboundEmailJob::dispatch($msg->id);
            }

            $created[] = $msg->id;
        });

        $this->newLine(2);
        $this->info("✓ Created {$count} inbound email(s) (IDs: " . implode(', ', array_slice($created, 0, 10)) . (count($created) > 10 ? '…' : '') . ')');
        $this->line('  View at: ' . url('/inbox'));

        return self::SUCCESS;
    }

    private function resolveSingleSender(Collection $allSenders): ?SolicitorEmailSender
    {
        $toEmail = $this->option('to');

        if ($toEmail) {
            $sender = $allSenders->firstWhere('email_address', strtolower(trim($toEmail)));
            if (!$sender) {
                $this->error("No SolicitorEmailSender found with address: {$toEmail}");
                return null;
            }
            $this->line("  <info>→</info> Inbox: <comment>{$sender->email_address}</comment>");
            return $sender;
        }

        $choices = $allSenders->mapWithKeys(fn ($s) => [
            $s->email_address => sprintf(
                '%s  [%s]',
                $s->email_address,
                $s->solicitor?->name ?? "Solicitor #{$s->solicitor_id}"
            ),
        ])->all();

        $chosen = $this->choice('Which inbox should receive the email?', array_values($choices));
        $email  = array_search($chosen, $choices);
        $sender = $allSenders->firstWhere('email_address', $email);

        $this->line("  <info>→</info> Inbox: <comment>{$sender->email_address}</comment>");
        return $sender;
    }

    private function createMessage(
        SolicitorEmailSender $sender,
        string $from,
        string $subject,
        string $body,
        mixed $readAt = null
    ): ContactMessage {
        return ContactMessage::create([
            'solicitor_id'    => $sender->solicitor_id,
            'sender_email_id' => $sender->id,
            'channel'         => MessageChannel::EMAIL,
            'direction'       => MessageDirection::INBOUND,
            'provider'        => MessageProvider::SENDGRID,
            'status'          => MessageStatus::SENT,
            'from_email'      => $from,
            'recipient_email' => $sender->email_address,
            'subject'         => $subject,
            'body_text'       => $body,
            'body_html'       => '<p>' . nl2br(e($body)) . '</p>',
            'read_at'         => $readAt,
        ]);
    }

    /**
     * Build a minimal but valid PDF containing the given text, store it on disk,
     * and create a ContactMessageAttachment record with extracted_text pre-filled
     * (extraction_status = 'completed') so re-classification works immediately.
     */
    private function createFakeAttachment(ContactMessage $message, string $filename, string $content): void
    {
        $pdfBytes = $this->buildMinimalPdf($content);
        $path     = "contact-attachments/{$message->id}/{$filename}";

        Storage::disk('local')->put($path, $pdfBytes);

        ContactMessageAttachment::create([
            'contact_message_id' => $message->id,
            'disk'               => 'local',
            'path'               => $path,
            'filename'           => $filename,
            'mime'               => 'application/pdf',
            'size'               => strlen($pdfBytes),
            'extracted_text'     => $content,
            'extraction_status'  => 'completed',
        ]);
    }

    /**
     * Build a minimal valid PDF-1.4 file containing the given plain text.
     * Uses Helvetica (a standard PDF base font — no embedding needed) and wraps
     * long lines so content fits within the page width.
     */
    private function buildMinimalPdf(string $text): string
    {
        // Wrap at 90 chars and sanitise PDF string literal special characters
        $lines = explode("\n", wordwrap($text, 90, "\n", false));

        $stream = "BT\n/F1 10 Tf\n50 780 Td\n13 TL\n";
        foreach ($lines as $line) {
            $escaped = str_replace(['\\', '(', ')'], ['\\\\', '\\(', '\\)'], $line);
            $stream .= "({$escaped}) Tj T*\n";
        }
        $stream .= "ET";

        $obj1 = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n";
        $obj2 = "2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n";
        $obj3 = "3 0 obj\n"
              . "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 842]\n"
              . "   /Contents 4 0 R\n"
              . "   /Resources << /Font << /F1 << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> >> >> >>\n"
              . "endobj\n";
        $obj4 = "4 0 obj\n<< /Length " . strlen($stream) . " >>\nstream\n{$stream}\nendstream\nendobj\n";

        $header  = "%PDF-1.4\n";
        $o1Start = strlen($header);
        $o2Start = $o1Start + strlen($obj1);
        $o3Start = $o2Start + strlen($obj2);
        $o4Start = $o3Start + strlen($obj3);
        $xref    = $o4Start + strlen($obj4);

        $body = $header . $obj1 . $obj2 . $obj3 . $obj4;

        $body .= "xref\n0 5\n";
        $body .= "0000000000 65535 f \n";
        $body .= str_pad($o1Start, 10, '0', STR_PAD_LEFT) . " 00000 n \n";
        $body .= str_pad($o2Start, 10, '0', STR_PAD_LEFT) . " 00000 n \n";
        $body .= str_pad($o3Start, 10, '0', STR_PAD_LEFT) . " 00000 n \n";
        $body .= str_pad($o4Start, 10, '0', STR_PAD_LEFT) . " 00000 n \n";
        $body .= "trailer\n<< /Size 5 /Root 1 0 R >>\n";
        $body .= "startxref\n{$xref}\n%%EOF";

        return $body;
    }
}
