<?php
declare(strict_types=1);

namespace App\Services;

use App\Core\Auth;
use App\Core\Database;
use App\Models\AuditLog;
use App\Models\Campaign;
use App\Models\CampaignRecipient;
use App\Models\MarketingConnection;
use App\Models\MarketingList;
use App\Models\Unsubscribe;
use RuntimeException;

final class CampaignSender
{
    public static function send(int $campaignId): array
    {
        $campaign = Campaign::find($campaignId);
        if (!$campaign) {
            throw new RuntimeException('Campaign not found.');
        }
        if ($campaign['status'] === 'sent') {
            throw new RuntimeException('This campaign was already sent.');
        }
        $list = MarketingList::find((int) $campaign['list_id']);
        if (!$list) {
            throw new RuntimeException('Pick a list before sending.');
        }
        $filters = IndustryService::decodeAttrs($list['filters'] ?? '{}');
        $contacts = ListQuery::contacts($filters);
        if ($contacts === []) {
            throw new RuntimeException('That list has nobody to email right now.');
        }

        $provider = MarketingConnection::activeProvider();
        $createdBy = (int) Auth::instance()->id();
        $tenantId = (int) Auth::instance()->tenantId();
        $mailOn = (bool) config('mail.enabled');
        $from = (string) config('mail.from_address', 'noreply@localhost');
        $sent = 0;
        $skipped = 0;

        foreach ($contacts as $contact) {
            $email = strtolower(trim((string) $contact['email']));
            if (Unsubscribe::isBlocked($email)) {
                $skipped++;
                continue;
            }
            $token = bin2hex(random_bytes(32));
            $subject = MergeService::apply((string) $campaign['subject'], $contact);
            $body = MergeService::apply((string) $campaign['body'], $contact);
            $unsubUrl = url('unsubscribe/' . $token);
            $openUrl = url('track/open/' . $token);
            $html = $body . '<p style="font-size:12px;color:#6b7280">If you do not want these emails, <a href="' . e($unsubUrl) . '">unsubscribe</a>.</p>'
                . '<img src="' . e($openUrl) . '" width="1" height="1" alt="">';

            $mailed = false;
            if ($mailOn) {
                $mailed = @mail($email, $subject, strip_tags($body), 'From: ' . $from . "\r\nContent-Type: text/html; charset=UTF-8");
            }

            CampaignRecipient::create([
                'campaign_id' => $campaignId,
                'contact_id' => (int) $contact['id'],
                'account_id' => (int) $contact['account_id'],
                'email' => $email,
                'token' => $token,
                'status' => 'sent',
                'sent_at' => date('Y-m-d H:i:s'),
            ]);
            RecordService::logActivity(
                'campaign',
                'Campaign sent: ' . $campaign['name'],
                $subject,
                'account',
                (int) $contact['account_id']
            );
            $sent++;
            unset($html, $mailed);
        }

        Campaign::update($campaignId, [
            'status' => 'sent',
            'sent_at' => date('Y-m-d H:i:s'),
            'provider' => $provider,
        ]);
        AuditLog::write('campaign.send', 'campaign', $campaignId, ['sent' => $sent, 'skipped' => $skipped]);
        return ['sent' => $sent, 'skipped' => $skipped];
    }

    public static function recordOpen(string $token): void
    {
        $row = CampaignRecipient::byToken($token);
        if (!$row || $row['status'] === 'bounced') {
            return;
        }
        if ($row['opened_at']) {
            return;
        }
        Database::instance()->query(
            'UPDATE campaign_recipients SET opened_at = NOW() WHERE id = :id AND opened_at IS NULL',
            ['id' => (int) $row['id']]
        );
        $campaign = Campaign::findInTenant((int) $row['campaign_id'], (int) $row['tenant_id']);
        $ownerId = (int) ($campaign['created_by'] ?? $row['tenant_id']);
        self::writeActivity(
            (int) $row['tenant_id'],
            $ownerId,
            'Opened: ' . (string) ($campaign['name'] ?? 'campaign'),
            (int) $row['account_id']
        );
    }

    public static function recordClick(string $token): ?string
    {
        $row = CampaignRecipient::byToken($token);
        if (!$row) {
            return null;
        }
        $campaign = Campaign::findInTenant((int) $row['campaign_id'], (int) $row['tenant_id']);
        if (!$row['clicked_at']) {
            Database::instance()->query(
                'UPDATE campaign_recipients SET clicked_at = NOW(), opened_at = COALESCE(opened_at, NOW()) WHERE id = :id',
                ['id' => (int) $row['id']]
            );
            self::writeActivity(
                (int) $row['tenant_id'],
                (int) ($campaign['created_by'] ?? 0),
                'Clicked: ' . (string) ($campaign['name'] ?? 'campaign'),
                (int) $row['account_id']
            );
        }
        $url = (string) ($row['click_url'] ?: '');
        if ($url === '' && $campaign && preg_match('#https?://[^\s"<]+#', (string) $campaign['body'], $m)) {
            $url = $m[0];
        }
        return self::safeUrl($url);
    }

    public static function unsubscribe(string $token): bool
    {
        $row = CampaignRecipient::byToken($token);
        if (!$row) {
            return false;
        }
        Unsubscribe::add((int) $row['tenant_id'], (string) $row['email'], 'campaign', (int) $row['campaign_id']);
        Database::instance()->query(
            "UPDATE campaign_recipients SET status = 'unsubscribed' WHERE id = :id",
            ['id' => (int) $row['id']]
        );
        return true;
    }

    private static function writeActivity(int $tenantId, int $ownerId, string $subject, int $accountId): void
    {
        if ($ownerId < 1) {
            $ownerId = 1;
        }
        Database::instance()->query(
            'INSERT INTO activities (tenant_id, type, subject, related_type, related_id, owner_id, activity_at)
             VALUES (:tid, :type, :subject, :rt, :rid, :oid, NOW())',
            [
                'tid' => $tenantId,
                'type' => 'campaign',
                'subject' => mb_substr($subject, 0, 180),
                'rt' => 'account',
                'rid' => $accountId,
                'oid' => $ownerId,
            ]
        );
    }

    private static function safeUrl(string $url): ?string
    {
        $url = trim($url);
        if ($url === '') {
            return null;
        }
        $parts = parse_url($url);
        $scheme = strtolower((string) ($parts['scheme'] ?? ''));
        if (!in_array($scheme, ['http', 'https'], true)) {
            return null;
        }
        return $url;
    }
}
