<?php
declare(strict_types=1);

namespace App\Admin;

use App\Questions\AnswerService;
use App\Questions\QuestionService;
use App\Topics\TopicService;
use App\Support\TranslatableException;
use PDO;
use Throwable;

final class EditorialSeedService
{
    public function __construct(private readonly PDO $pdo) {}

    public function createPersona(int $actorAdminId, string $name, string $email, string $locale): int
    {
        $this->requireAdmin($actorAdminId);
        return (new AdminRepository($this->pdo))->createEditorialPersona($name, $email, $locale, $actorAdminId);
    }

    /** @return list<array<string,mixed>> */
    public function personas(): array
    {
        return $this->pdo->query("SELECT id,name,email,preferred_locale,trust_level,created_at FROM users WHERE account_type='editorial' AND status='active' ORDER BY name ASC,id ASC")->fetchAll(PDO::FETCH_ASSOC);
    }

    /** @param list<int> $tagIds */
    public function createQuestion(int $actorAdminId, int $personaId, string $title, string $body, ?int $categoryId, string $locale, array $tagIds = [], string $status = 'published'): int
    {
        $this->requireAdmin($actorAdminId);
        $this->requirePersona($personaId);
        if ($this->questionDuplicate($personaId, $title, $body)) throw new TranslatableException('admin.seed_duplicate');
        return (new QuestionService($this->pdo))->create($personaId, $title, $body, $categoryId, $locale, false, $tagIds, $status);
    }

    public function createAnswer(int $actorAdminId, int $personaId, int $questionId, string $body, string $status = 'published'): int
    {
        $this->requireAdmin($actorAdminId);
        $this->requirePersona($personaId);
        if ($this->answerDuplicate($personaId, $questionId, $body)) throw new TranslatableException('admin.seed_duplicate');
        return (new AnswerService($this->pdo))->create($personaId, $questionId, $body, $status);
    }

    public function createTopic(int $actorAdminId, int $personaId, string $title, string $body, ?int $categoryId, string $locale, string $kind = 'discussion', string $status = 'published'): int
    {
        $this->requireAdmin($actorAdminId);
        $this->requirePersona($personaId);
        // Editorial personas intentionally remain ordinary non-login members. Announcements stay a staff action.
        if ($kind !== 'discussion') throw new TranslatableException('admin.seed_topic_kind');
        if ($this->topicDuplicate($personaId, $title, $body)) throw new TranslatableException('admin.seed_duplicate');
        return (new TopicService($this->pdo))->create($personaId, $title, $body, $categoryId, $locale, 'discussion', $status);
    }

    /**
     * @param list<array<string,mixed>> $rows
     * @return array{new:int,duplicate:int,invalid:int,unknown:int,created:list<array{type:string,id:int}>,errors:list<string>,dry_run:bool}
     */
    public function importRows(int $actorAdminId, array $rows, bool $dryRun = true): array
    {
        $this->requireAdmin($actorAdminId);
        $result=['new'=>0,'duplicate'=>0,'invalid'=>0,'unknown'=>0,'created'=>[],'errors'=>[],'dry_run'=>$dryRun];
        foreach ($rows as $index=>$row) {
            if (!is_array($row)) { $result['invalid']++; $result['errors'][]='Row '.($index+1).': invalid row.'; continue; }
            try {
                $resolved=$this->resolveRow($row);
                if ($this->isDuplicateResolved($resolved)) { $result['duplicate']++; continue; }
                $result['new']++;
                if ($dryRun) continue;
                $owns=!$this->pdo->inTransaction(); if($owns)$this->pdo->beginTransaction();
                try {
                    $id=match($resolved['type']) {
                        'question'=>$this->createQuestion($actorAdminId,$resolved['persona_id'],$resolved['title'],$resolved['body'],$resolved['category_id'],$resolved['locale'],$resolved['tag_ids'],$resolved['status']),
                        'answer'=>$this->createAnswer($actorAdminId,$resolved['persona_id'],$resolved['question_id'],$resolved['body'],$resolved['status']),
                        'topic'=>$this->createTopic($actorAdminId,$resolved['persona_id'],$resolved['title'],$resolved['body'],$resolved['category_id'],$resolved['locale'],'discussion',$resolved['status']),
                    };
                    if($owns)$this->pdo->commit();
                    $result['created'][]=['type'=>$resolved['type'],'id'=>$id];
                } catch(Throwable $e) { if($owns&&$this->pdo->inTransaction())$this->pdo->rollBack(); throw $e; }
            } catch (TranslatableException $e) {
                $key=$e->translationKey;
                if (in_array($key,['admin.seed_persona_unknown','admin.seed_category_unknown','admin.seed_tags_unknown','admin.seed_question_unknown'],true)) $result['unknown']++; else $result['invalid']++;
                $result['errors'][]='Row '.($index+1).': '.$key;
            } catch (Throwable $e) {
                $result['invalid']++; $result['errors'][]='Row '.($index+1).': '.$e->getMessage();
            }
        }
        return $result;
    }

    /** @param list<array<string,mixed>> $rows */
    public function dryRun(int $actorAdminId, array $rows): array { return $this->importRows($actorAdminId,$rows,true); }

    /** @param array<string,mixed> $row @return array<string,mixed> */
    private function resolveRow(array $row): array
    {
        $type=strtolower(trim((string)($row['type']??'')));
        if(!in_array($type,['question','answer','topic'],true))throw new TranslatableException('admin.seed_type_invalid');
        $personaId=$this->resolvePersona($row['persona_id']??$row['persona']??null);
        $body=trim((string)($row['body']??'')); if($body==='')throw new TranslatableException('admin.seed_body_required');
        $title=trim((string)($row['title']??'')); if($type!=='answer'&&$title==='')throw new TranslatableException('admin.seed_title_required');
        $locale=strtolower(str_replace('_','-',trim((string)($row['locale']??'ar'))));
        if(!preg_match('/^[a-z]{2,8}(?:-[a-z0-9]{2,8})?$/',$locale))throw new TranslatableException('admin.seed_locale_invalid');
        $status=strtolower(trim((string)($row['status']??'published'))); if(!in_array($status,['published','pending'],true))$status='published';
        $categoryId=$this->resolveCategory($row['category_id']??$row['category']??null);
        $questionId=$type==='answer'?$this->resolveQuestion($row['question_id']??null):0;
        $tagIds=$type==='question'?$this->resolveTags($row['tag_ids']??$row['tags']??[]):[];
        return compact('type','personaId','body','title','locale','status','categoryId','questionId','tagIds')+[
            'persona_id'=>$personaId,'category_id'=>$categoryId,'question_id'=>$questionId,'tag_ids'=>$tagIds,
        ];
    }

    private function resolvePersona(mixed $value): int
    {
        if(is_numeric((string)$value)){$id=(int)$value;$s=$this->pdo->prepare("SELECT id FROM users WHERE id=:id AND account_type='editorial' AND login_disabled=1 AND status='active' LIMIT 1");$s->execute(['id'=>$id]);$found=$s->fetchColumn();}
        else{$needle=trim((string)$value);$s=$this->pdo->prepare("SELECT id FROM users WHERE account_type='editorial' AND login_disabled=1 AND status='active' AND (name=:needle OR email=:needle) LIMIT 1");$s->execute(['needle'=>$needle]);$found=$s->fetchColumn();}
        if($found===false)throw new TranslatableException('admin.seed_persona_unknown');return (int)$found;
    }

    private function resolveCategory(mixed $value): ?int
    {
        if($value===null||trim((string)$value)==='')return null;
        if(is_numeric((string)$value)){$s=$this->pdo->prepare('SELECT id FROM categories WHERE id=:id LIMIT 1');$s->execute(['id'=>(int)$value]);}
        else{$s=$this->pdo->prepare('SELECT id FROM categories WHERE slug=:value OR name=:value LIMIT 1');$s->execute(['value'=>trim((string)$value)]);}
        $id=$s->fetchColumn();if($id===false)throw new TranslatableException('admin.seed_category_unknown');return (int)$id;
    }

    private function resolveQuestion(mixed $value): int
    {
        $id=max(0,(int)$value);if($id<=0)throw new TranslatableException('admin.seed_question_unknown');
        $s=$this->pdo->prepare("SELECT id FROM questions WHERE id=:id AND status='published' LIMIT 1");$s->execute(['id'=>$id]);if($s->fetchColumn()===false)throw new TranslatableException('admin.seed_question_unknown');return $id;
    }

    /** @return list<int> */
    private function resolveTags(mixed $value): array
    {
        $values=is_array($value)?$value:(preg_split('/[,;]+/',trim((string)$value))?:[]);$ids=[];
        foreach($values as $item){$item=trim((string)$item);if($item==='')continue;if(ctype_digit($item)){$s=$this->pdo->prepare('SELECT id FROM tags WHERE id=:id LIMIT 1');$s->execute(['id'=>(int)$item]);}else{$s=$this->pdo->prepare('SELECT id FROM tags WHERE slug=:value OR name=:value LIMIT 1');$s->execute(['value'=>$item]);}$id=$s->fetchColumn();if($id===false)throw new TranslatableException('admin.seed_tags_unknown');$ids[]=(int)$id;}
        return array_slice(array_values(array_unique($ids)),0,5);
    }

    /** @param array<string,mixed> $resolved */
    private function isDuplicateResolved(array $resolved): bool
    {
        return match($resolved['type']){
            'question'=>$this->questionDuplicate($resolved['persona_id'],$resolved['title'],$resolved['body']),
            'answer'=>$this->answerDuplicate($resolved['persona_id'],$resolved['question_id'],$resolved['body']),
            'topic'=>$this->topicDuplicate($resolved['persona_id'],$resolved['title'],$resolved['body']),
        };
    }
    private function questionDuplicate(int $uid,string $title,string $body):bool{$s=$this->pdo->prepare('SELECT 1 FROM questions WHERE user_id=:uid AND title=:title AND body=:body LIMIT 1');$s->execute(compact('uid','title','body'));return $s->fetchColumn()!==false;}
    private function answerDuplicate(int $uid,int $questionId,string $body):bool{$s=$this->pdo->prepare('SELECT 1 FROM answers WHERE user_id=:uid AND question_id=:question_id AND body=:body LIMIT 1');$s->execute(['uid'=>$uid,'question_id'=>$questionId,'body'=>$body]);return $s->fetchColumn()!==false;}
    private function topicDuplicate(int $uid,string $title,string $body):bool{$s=$this->pdo->prepare('SELECT 1 FROM topics WHERE user_id=:uid AND title=:title AND body=:body LIMIT 1');$s->execute(compact('uid','title','body'));return $s->fetchColumn()!==false;}

    private function requirePersona(int $id): void
    {
        $s=$this->pdo->prepare("SELECT id FROM users WHERE id=:id AND account_type='editorial' AND login_disabled=1 AND status='active' LIMIT 1");$s->execute(['id'=>$id]);if($s->fetchColumn()===false)throw new TranslatableException('admin.seed_persona_unknown');
    }
    private function requireAdmin(int $id): void
    {
        $s=$this->pdo->prepare("SELECT id FROM users WHERE id=:id AND role='admin' AND status='active' LIMIT 1");$s->execute(['id'=>$id]);if($s->fetchColumn()===false)throw new TranslatableException('admin.admin_only');
    }
}
