<?php

namespace App\Services;

use App\Helpers\LanguageHelper;
use App\Models\Franchisee;
use App\Models\Program;
use App\Models\WhatsappMessage;
use App\Models\WhatsappMessageTranslation;
use Illuminate\Support\Collection;

/**
 * Phase-2 runtime resolver. Turns a Zoho Account id (the UTM campaign's Account.id,
 * which equals franchisees.zoho_franchise_id) into a fully self-contained, translated
 * message flow the api-connect WhatsApp runner can walk WITHOUT calling back per step.
 *
 *   account_id
 *     -> Franchisee::where('zoho_franchise_id', account_id)
 *     -> WhatsappMessage::effectiveFlowFor(franchisee)   (own forked flow, else global default)
 *     -> translate to the lead's language (fallback to base text when untranslated)
 *     -> resolve age->program and program->media so the runner needs no further lookups
 *
 * When no franchisee matches the account id (or it has no flow), the GLOBAL default flow
 * is returned with scope = "default" — this preserves today's behaviour for everyone else.
 */
class WhatsappFlowResolver
{
    /**
     * Resolve the flow for an account id in the given language.
     *
     * @return array{account_id:?string,franchisee_id:?string,scope:string,language:string,messages:array}
     */
    public function resolve(?string $accountId, ?string $lang = null): array
    {
        $franchisee = $accountId
            ? Franchisee::where('zoho_franchise_id', $accountId)->first()
            : null;

        $franchiseeId = $franchisee?->id;

        $messages = WhatsappMessage::effectiveFlowFor($franchiseeId)
            ->where('is_active', true)
            ->values();

        // scope reflects what the runner actually got: a franchisee's OWN forked flow,
        // or the shared global default (no match, or matched but never customised).
        $scope = ($franchiseeId && $messages->isNotEmpty() && $messages->first()->franchisee_id === $franchiseeId)
            ? 'own'
            : 'default';

        $normalized = LanguageHelper::normalizeToCodeName($lang ?: '');
        $code = $normalized['code'] ?? 'en';

        $translations = $this->translationsFor($franchiseeId, $messages, $code);

        $resolved = $messages
            ->map(fn(WhatsappMessage $m) => $this->resolveMessage($m, $messages, $translations, $franchiseeId, $code))
            ->values()
            ->all();

        return [
            'account_id' => $accountId,
            'franchisee_id' => $franchiseeId,
            'scope' => $scope,
            'language' => $code,
            'messages' => $resolved,
        ];
    }

    /**
     * Franchisee translation rows for these messages in one query, keyed by message id.
     * Only franchisee-owned flows have translations; the global default returns base text.
     */
    private function translationsFor(?string $franchiseeId, Collection $messages, string $code): Collection
    {
        if (!$franchiseeId || $messages->isEmpty()) {
            return collect();
        }

        return WhatsappMessageTranslation::where('franchisee_id', $franchiseeId)
            ->where('language_code', $code)
            ->whereIn('whatsapp_message_id', $messages->pluck('id'))
            ->get()
            ->keyBy('whatsapp_message_id');
    }

    /**
     * One message -> a flat, runner-ready shape. Text fields use the translation when
     * present, else the base. Age/program questions get their runtime extras resolved.
     */
    private function resolveMessage(
        WhatsappMessage $m,
        Collection $flow,
        Collection $translations,
        ?string $franchiseeId,
        string $code
    ): array {
        $t = $translations->get($m->id);

        $out = [
            'id' => $m->id,
            'order_no' => $m->order_no,
            'type' => $m->type,
            'question_type' => $m->question_type,
            'key' => $m->key,
            'header_text' => $t->header_text ?? $m->header_text,
            'body_text' => $t->body_text ?? $m->body_text,
            'footer_text' => $t->footer_text ?? $m->footer_text,
            'options' => $t->options ?? $m->options,
            'media_url' => $m->media_url,
            'expects' => $m->expects,
            // Set when this is a per-program media message (video/image mapped to a program).
            // The runner skips these in the linear walk — they're sent via program selection
            // (embedded in each program_map entry's `media`) instead.
            'program_id' => $m->program_id,
        ];

        if ($m->question_type === WhatsappMessage::QUESTION_AGE) {
            // The age boxes; `options` (above) already carries the WhatsApp list rows as
            // [{id,title}] where id IS the age box id, so a list reply maps straight to age_id.
            $out['age_options'] = array_values($m->age_options ?? []);
        }

        if ($m->question_type === WhatsappMessage::QUESTION_PROGRAM) {
            $out['program_map'] = $this->resolveProgramMap($m, $flow, $franchiseeId, $code);
        }

        return $out;
    }

    /**
     * Expand a program question's stored map (age_id -> [global program ids]) into concrete,
     * translated programs, each carrying the media message mapped to it. The runner can then,
     * given the chosen age box, list the programs and — once one is chosen — send its media
     * with no further round-trip.
     *
     * @return array<int,array{age_id:string,programs:array}>
     */
    private function resolveProgramMap(
        WhatsappMessage $program,
        Collection $flow,
        ?string $franchiseeId,
        string $code
    ): array {
        $out = [];

        foreach ((array) ($program->program_map ?? []) as $row) {
            $ageId = (string) ($row['age_id'] ?? '');
            if ($ageId === '') {
                continue;
            }

            $programs = collect($row['program_ids'] ?? [])
                ->map(fn($gid) => $this->resolveProgram((string) $gid, $flow, $franchiseeId, $code))
                ->filter()
                ->values()
                ->all();

            $out[] = ['age_id' => $ageId, 'programs' => $programs];
        }

        return $out;
    }

    /**
     * A single global program id -> the caller's effective program (override row if any),
     * translated, plus the media (video/image) message mapped to it in this flow.
     */
    private function resolveProgram(string $globalId, Collection $flow, ?string $franchiseeId, string $code): ?array
    {
        $program = null;
        if ($franchiseeId) {
            $program = Program::where('franchisee_id', $franchiseeId)
                ->where(fn($q) => $q->where('id', $globalId)->orWhere('parent_program_id', $globalId))
                ->with('translations')
                ->first();
        }
        if (!$program) {
            $program = Program::where('is_global', true)->whereNull('franchisee_id')
                ->where('id', $globalId)
                ->with('translations')
                ->first();
        }
        if (!$program) {
            return null;
        }

        $title = $program->translationFor($code, $franchiseeId)?->title
            ?: $program->translationFor('en', $franchiseeId)?->title
            ?: $program->slug;

        return [
            'id' => $program->id,
            'global_id' => $program->parent_program_id ?: $program->id,
            'slug' => $program->slug,
            'title' => $title,
            'media' => $this->mediaForProgram($globalId, $flow),
        ];
    }

    /**
     * The media (video/image) message in this flow mapped to a program (by its GLOBAL id),
     * sent after that program is chosen. Null when the flow has no media for the program.
     */
    private function mediaForProgram(string $globalId, Collection $flow): ?array
    {
        $media = $flow->first(fn(WhatsappMessage $m) => $m->program_id === $globalId
            && in_array($m->type, [WhatsappMessage::TYPE_VIDEO, WhatsappMessage::TYPE_IMAGE], true));

        if (!$media) {
            return null;
        }

        return [
            'id' => $media->id,
            'type' => $media->type,
            'media_url' => $media->media_url,
            'body_text' => $media->body_text,
        ];
    }
}
