<?php

namespace App\Controllers;

use App\Models\CampaignModel;
use App\Models\CampaignRecipientModel;
use App\Models\GuestModel;
use Config\Lookups;

/**
 * Email marketing campaigns — manual send only (the actual ask was
 * "email marketing integration", not an automation engine). Manager-only,
 * same tier as Settings. Reuses GuestModel::filterList() for audience
 * targeting and EmailService for sending — no new mail infrastructure,
 * no new filtering logic duplicated from the Guest CRM list.
 */
class CampaignController extends BaseController
{
    public function index()
    {
        return view('campaigns/index', [
            'pageTitle' => 'Email Campaigns',
            'campaigns' => (new CampaignModel())->withCreator()->findAll(),
            'lookups'   => new Lookups(),
        ]);
    }

    public function new()
    {
        return view('campaigns/form', [
            'pageTitle'     => 'New Campaign',
            'nationalities' => (new GuestModel())->distinctNationalities(),
            'lookups'       => new Lookups(),
        ]);
    }

    /** Saves as a draft — never sends. Sending is its own explicit, reviewable step (see send()). */
    public function create()
    {
        $campaigns = new CampaignModel();
        $data = [
            'subject'         => $this->request->getPost('subject'),
            'body'            => $this->request->getPost('body'),
            'audience_filter' => json_encode($this->collectFilter()),
            'status'          => 'draft',
            'created_by'      => $this->currentUserId(),
        ];

        if (! $campaigns->insert($data)) {
            return $this->redirectWithModelErrors($campaigns);
        }

        $campaignId = $campaigns->getInsertID();
        $this->logActivity('communication', "Drafted email campaign \"{$data['subject']}\".");

        return redirect()->to('campaigns/' . $campaignId)->with('success', 'Campaign saved as a draft.');
    }

    public function show(int $id)
    {
        $campaign = (new CampaignModel())->find($id);

        if ($campaign === null) {
            return redirect()->to('campaigns')->with('error', 'Campaign not found.');
        }

        $filter = (new CampaignModel())->decodedFilter($campaign);

        return view('campaigns/show', [
            'pageTitle'      => $campaign->subject,
            'campaign'       => $campaign,
            'filter'         => $filter,
            'audienceCount'  => $this->audienceQuery($filter)->countAllResults(),
            'recipients'     => (new CampaignRecipientModel())->forCampaign($id),
            'lookups'        => new Lookups(),
        ]);
    }

    /** Live "N guests will receive this" count while composing, before anything is saved or sent. */
    public function previewAudience()
    {
        $count = $this->audienceQuery($this->collectFilter())->countAllResults();

        return $this->response->setJSON(['count' => $count]);
    }

    /**
     * The one real send path — a batch loop through EmailService, same as
     * every other email in this app. A draft can only ever be sent once
     * (status flips to 'sent' immediately); re-sending means creating a
     * new campaign, so there's no risk of double-emailing an audience by
     * clicking twice.
     */
    public function send(int $id)
    {
        $campaigns = new CampaignModel();
        $campaign  = $campaigns->find($id);

        if ($campaign === null) {
            return redirect()->to('campaigns')->with('error', 'Campaign not found.');
        }

        if ($campaign->status === 'sent') {
            return redirect()->to('campaigns/' . $id)->with('error', 'This campaign has already been sent.');
        }

        $guests    = $this->audienceQuery((new CampaignModel())->decodedFilter($campaign))->findAll();
        $emailService = service('emailService');
        $recipients   = new CampaignRecipientModel();
        $sentCount    = 0;

        foreach ($guests as $guest) {
            $body   = str_replace('{name}', $guest->full_name, $campaign->body);
            $result = $emailService->send($guest->email, $campaign->subject, $body, null, $guest->id);

            $recipients->insert([
                'campaign_id' => $id,
                'guest_id'    => $guest->id,
                'status'      => $result['success'] ? 'sent' : 'failed',
                'error'       => $result['success'] ? null : $result['error'],
                'sent_at'     => date('Y-m-d H:i:s'),
            ]);

            if ($result['success']) {
                $sentCount++;
            }
        }

        $campaigns->update($id, [
            'status'     => 'sent',
            'sent_count' => $sentCount,
            'sent_at'    => date('Y-m-d H:i:s'),
        ]);

        $this->logActivity('communication', "Sent email campaign \"{$campaign->subject}\" to {$sentCount}/" . count($guests) . ' guests.');

        return redirect()->to('campaigns/' . $id)->with('success', "Sent to {$sentCount} of " . count($guests) . ' guests.');
    }

    /**
     * getVar() (not getPost()) since this is shared by create() — a POST
     * — and previewAudience() — a GET, used live while composing before
     * anything is saved. A real bug caught in testing: using getPost()
     * here made previewAudience() silently ignore every GET filter and
     * always return the full unfiltered audience count.
     */
    private function collectFilter(): array
    {
        return [
            'segment'     => $this->request->getVar('segment') ?: null,
            'nationality' => $this->request->getVar('nationality') ?: null,
        ];
    }

    /**
     * GuestModel::filterList() plus the two guarantees a marketing send
     * always needs regardless of what's in the filter: never a
     * blacklisted guest, never a guest with no email on file to send to.
     */
    private function audienceQuery(array $filter)
    {
        return (new GuestModel())->filterList($filter)
            ->where('is_blacklisted', 0)
            ->where('email IS NOT NULL')
            ->where('email !=', '');
    }
}
