<?php

declare(strict_types=1);

namespace App\Domain\MailingList\Services;

use App\Domain\Account\Models\Account;
use App\Domain\Email\Models\EmailForwarder;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * Mailing List (Phase 1 MVP) — multi-destination forwarder bazlı liste.
 *
 * Yaklaşım:
 *   email_forwarders satırı, destinations[0] === '__list__' olduğunda mailing list sayılır.
 *   Gerçek üyeler destinations[1..N] içinde tutulur.
 *
 * is_mailing_list flagi schema değişikliği yerine bu sentinel ile temsil edilir.
 *
 * Phase 2: gerçek Mailman entegrasyonu (subscribe link, archive, mod queue).
 */
class MailingListService
{
    public const LIST_SENTINEL = '__list__';

    /**
     * Yeni mailing list oluştur (= özel forwarder satırı).
     *
     * @param array<int, string> $members
     */
    public function createList(
        Account $account,
        string $listName,
        string $domain,
        array $members,
        bool $keepLocalCopy = false
    ): EmailForwarder {
        $listName = $this->normalizeLocal($listName);
        $source = $listName . '@' . $domain;

        // Üyeler — temizle, deduplike et, validate et
        $cleanMembers = $this->normalizeMembers($members);

        $forwarder = EmailForwarder::create([
            'account_id' => $account->id,
            'source' => $source,
            'destinations' => $this->encodeListDestinations($cleanMembers),
            'keep_local_copy' => $keepLocalCopy,
            'is_active' => true,
        ]);

        $this->recordAudit('mailinglist.created', [
            'list_id' => $forwarder->id,
            'source' => $source,
            'member_count' => count($cleanMembers),
        ], true);

        return $forwarder;
    }

    /**
     * Listeye üye ekle.
     */
    public function addMember(int $listId, string $email): EmailForwarder
    {
        $forwarder = $this->mustGetList($listId);

        $members = $this->getMembers($forwarder);
        $email = strtolower(trim($email));

        if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException('Geçersiz e-posta adresi: ' . $email);
        }

        if (! in_array($email, $members, true)) {
            $members[] = $email;
            $forwarder->destinations = $this->encodeListDestinations($members);
            $forwarder->save();
        }

        $this->recordAudit('mailinglist.member_added', [
            'list_id' => $listId,
            'email' => $email,
            'member_count' => count($members),
        ], true);

        return $forwarder->fresh();
    }

    /**
     * Listeden üye çıkar.
     */
    public function removeMember(int $listId, string $email): EmailForwarder
    {
        $forwarder = $this->mustGetList($listId);

        $members = $this->getMembers($forwarder);
        $email = strtolower(trim($email));

        $members = array_values(array_filter($members, fn ($m) => strtolower($m) !== $email));

        $forwarder->destinations = $this->encodeListDestinations($members);
        $forwarder->save();

        $this->recordAudit('mailinglist.member_removed', [
            'list_id' => $listId,
            'email' => $email,
            'member_count' => count($members),
        ], true);

        return $forwarder->fresh();
    }

    /**
     * Toplu üye değişikliği — verilen liste ile mevcut üyeleri replace et.
     *
     * @param array<int, string> $members
     */
    public function setMembers(int $listId, array $members): EmailForwarder
    {
        $forwarder = $this->mustGetList($listId);

        $cleanMembers = $this->normalizeMembers($members);

        $forwarder->destinations = $this->encodeListDestinations($cleanMembers);
        $forwarder->save();

        $this->recordAudit('mailinglist.members_replaced', [
            'list_id' => $listId,
            'member_count' => count($cleanMembers),
        ], true);

        return $forwarder->fresh();
    }

    /**
     * Listeyi sil.
     */
    public function deleteList(int $listId): bool
    {
        $forwarder = $this->mustGetList($listId);

        $this->recordAudit('mailinglist.deleted', [
            'list_id' => $listId,
            'source' => $forwarder->source,
        ], true);

        return (bool) $forwarder->delete();
    }

    /**
     * Tüm mailing list'leri (pagination'lı sorgu üzerinden alınabilir).
     */
    public function query(): Builder
    {
        return EmailForwarder::query()
            ->where(function ($q) {
                // destinations text JSON olduğundan LIKE ile sentinel ara
                $q->where('destinations', 'like', '["__list__"%')
                    ->orWhere('destinations', 'like', '%"__list__"%'); // tolerans
            });
    }

    /**
     * Bir mailing list satırı üyelerini diziye çevir.
     *
     * @return array<int, string>
     */
    public function getMembers(EmailForwarder $forwarder): array
    {
        $dests = $forwarder->destinations ?? [];
        if (! is_array($dests) || empty($dests)) {
            return [];
        }

        // İlk element sentinel olmalı — kalan üyeler
        if (($dests[0] ?? null) === self::LIST_SENTINEL) {
            return array_slice($dests, 1);
        }

        // Eski format / migration durumu — tümü üye say
        return $dests;
    }

    /**
     * Helper — bir forwarder mailing list mi?
     */
    public function isMailingList(EmailForwarder $forwarder): bool
    {
        $dests = $forwarder->destinations ?? [];

        return is_array($dests) && ($dests[0] ?? null) === self::LIST_SENTINEL;
    }

    /**
     * Liste bilgisini UI'a dökmek için flatten.
     */
    public function toArray(EmailForwarder $forwarder): array
    {
        $members = $this->getMembers($forwarder);

        return [
            'id' => $forwarder->id,
            'account_id' => $forwarder->account_id,
            'source' => $forwarder->source,
            'list_name' => explode('@', $forwarder->source)[0] ?? '',
            'domain' => explode('@', $forwarder->source)[1] ?? '',
            'members' => $members,
            'member_count' => count($members),
            'keep_local_copy' => (bool) $forwarder->keep_local_copy,
            'is_active' => (bool) $forwarder->is_active,
            'created_at' => $forwarder->created_at?->toIso8601String(),
            'updated_at' => $forwarder->updated_at?->toIso8601String(),
        ];
    }

    // ─── Helpers ─────────────────────────────────────────────────────────────

    private function mustGetList(int $listId): EmailForwarder
    {
        $forwarder = EmailForwarder::findOrFail($listId);

        if (! $this->isMailingList($forwarder)) {
            throw new \RuntimeException('Bu forwarder bir mailing list değil (id ' . $listId . ').');
        }

        return $forwarder;
    }

    /**
     * @param array<int, string> $members
     * @return array<int, string>
     */
    private function encodeListDestinations(array $members): array
    {
        return array_merge([self::LIST_SENTINEL], array_values($members));
    }

    /**
     * @param array<int, string> $members
     * @return array<int, string>
     */
    private function normalizeMembers(array $members): array
    {
        $clean = [];
        foreach ($members as $m) {
            $m = strtolower(trim((string) $m));
            if ($m === '' || ! filter_var($m, FILTER_VALIDATE_EMAIL)) {
                continue;
            }
            if (! in_array($m, $clean, true)) {
                $clean[] = $m;
            }
        }

        return $clean;
    }

    private function normalizeLocal(string $name): string
    {
        $name = strtolower(trim($name));
        $name = preg_replace('/[^a-z0-9._-]/', '', $name) ?? '';

        return $name ?: 'list';
    }

    private function recordAudit(string $action, array $payload, bool $success): void
    {
        try {
            if (Schema::hasTable('license_audit_log')) {
                DB::table('license_audit_log')->insert([
                    'action' => $action,
                    'payload' => json_encode($payload, JSON_UNESCAPED_UNICODE),
                    'success' => $success,
                    'remote_ip' => request()?->ip(),
                    'occurred_at' => now(),
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        } catch (\Throwable) {
            // ignored
        }
    }
}
