<?php

namespace App\Services\Member;

use App\Models\Survey;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

// ══════════════════════════════════════════════════════════════════
//  InPractice — SurveyService
//
//  Every member has their own personal question bank alongside
//  the platform-seeded bank. Personal items have user_id set.
//  Platform items have user_id = null.
//
//  builderData($userId) — loads both platform + personal items
//  saveToBank($userId, $questions) — saves flagged questions
//  deleteBankItem($userId, $itemId) — member deletes their own item
// ══════════════════════════════════════════════════════════════════

class SurveyService
{
    // ── Index ──────────────────────────────────────────────────
    public function index(int $userId): array
    {
        $mySurveys = Survey::where('created_by', $userId)
            ->withCount('questions')
            ->orderByDesc('created_at')
            ->get()
            ->map(fn($s) => $this->formatCard($s));

        $respondedIds = DB::table('survey_responses')
            ->where('user_id', $userId)
            ->pluck('survey_id');

        $openSurveys = Survey::where('status', 'active')
            ->where('created_by', '!=', $userId)
            ->whereNotIn('id', $respondedIds)
            ->withCount('questions')
            ->orderByDesc('created_at')
            ->get()
            ->map(fn($s) => $this->formatCard($s));

        return compact('mySurveys', 'openSurveys');
    }

    // ── Builder data ───────────────────────────────────────────
    // Returns platform sections + personal sections,
    // and platform items + personal items — all scoped correctly.
    public function builderData(int $userId): array
    {
        // Platform sections (user_id = null) + personal sections
        $bankSections = DB::table('question_bank_sections')
            ->where(function ($q) use ($userId) {
                $q->whereNull('user_id')
                  ->orWhere('user_id', $userId);
            })
            ->orderByRaw('user_id IS NOT NULL') // platform first
            ->orderBy('sort_order')
            ->get()
            ->map(fn($s) => [
                'id'         => $s->id,
                'name_en'    => $s->name_en,
                'name_ar'    => $s->name_ar,
                'color'      => $s->color,
                'is_personal' => !is_null($s->user_id),
            ]);

        // Platform items (user_id = null) + personal items
        $bankItems = DB::table('question_bank_items as qi')
            ->where(function ($q) use ($userId) {
                $q->whereNull('qi.user_id')
                  ->orWhere('qi.user_id', $userId);
            })
            ->select('qi.*')
            ->orderByRaw('qi.user_id IS NOT NULL') // platform first
            ->orderBy('qi.question_bank_section_id')
            ->orderByDesc('qi.usage_count')
            ->get()
            ->map(fn($item) => [
                'id'                       => $item->id,
                'question_bank_section_id' => $item->question_bank_section_id,
                'user_id'                  => $item->user_id,
                'is_personal'              => !is_null($item->user_id),
                'question_en'              => $item->question_en,
                'question_ar'              => $item->question_ar,
                'type'                     => $item->type,
                'options_en'               => $item->options_en ? json_decode($item->options_en) : [],
                'options_ar'               => $item->options_ar ? json_decode($item->options_ar) : [],
                'rating_min'               => $item->rating_min,
                'rating_max'               => $item->rating_max,
                'is_required'              => (bool) $item->is_required,
                'placeholder_en'           => $item->placeholder_en,
                'placeholder_ar'           => $item->placeholder_ar,
                'usage_count'              => $item->usage_count,
            ]);

        return compact('bankSections', 'bankItems');
    }

    // ── Save questions to personal bank ───────────────────────
    // Called inside store() and update().
    // Only processes questions where _save_to_bank = true.
    public function saveToBank(int $userId, array $questions): void
    {
        foreach ($questions as $q) {
            if (empty($q['_save_to_bank'])) continue;
            if (empty(trim($q['question_en'] ?? ''))) continue;

            DB::table('question_bank_items')->insert([
                'user_id'                  => $userId,
                'question_bank_section_id' => $q['_bank_section_id'] ?? null,
                'question_en'              => $q['question_en'],
                'question_ar'              => $q['question_ar'] ?? null,
                'type'                     => $q['type'],
                'options_en'               => isset($q['options_en']) && count($q['options_en'])
                                                ? json_encode(array_values(array_filter($q['options_en'], fn($o) => trim($o) !== '')))
                                                : null,
                'options_ar'               => isset($q['options_ar']) && count($q['options_ar'])
                                                ? json_encode(array_values(array_filter($q['options_ar'], fn($o) => trim($o) !== '')))
                                                : null,
                'rating_min'               => $q['rating_min'] ?? 1,
                'rating_max'               => $q['rating_max'] ?? 5,
                'is_required'              => $q['is_required'] ?? true,
                'usage_count'              => 0,
                'created_at'               => now(),
                'updated_at'               => now(),
            ]);
        }
    }

    // ── Delete personal bank item ──────────────────────────────
    // Member can only delete their own items — user_id check enforced.
    public function deleteBankItem(int $userId, int $itemId): bool
    {
        $deleted = DB::table('question_bank_items')
            ->where('id', $itemId)
            ->where('user_id', $userId) // cannot delete platform items
            ->delete();

        return $deleted > 0;
    }

    // ── Format survey for edit form ────────────────────────────
    public function formatSurveyForEdit(Survey $survey): array
    {
        $questions = $survey->questions->map(fn($q) => [
            'id'          => $q->id,
            'question_en' => $q->question_en,
            'question_ar' => $q->question_ar,
            'type'        => $q->type,
            'options_en'  => $q->options_en ?? [],
            'options_ar'  => $q->options_ar ?? [],
            'rating_min'  => $q->rating_min,
            'rating_max'  => $q->rating_max,
            'is_required' => $q->is_required,
            'sort_order'  => $q->sort_order,
        ]);

        return [
            'id'                      => $survey->id,
            'title_en'                => $survey->title_en,
            'title_ar'                => $survey->title_ar,
            'description_en'          => $survey->description_en,
            'description_ar'          => $survey->description_ar,
            'audience'                => $survey->audience,
            'hub_id'                  => $survey->hub_id,
            'anonymous_responses'     => $survey->anonymous_responses,
            'show_results_to_members' => $survey->show_results_to_members,
            'closes_at'               => $survey->closes_at?->format('Y-m-d'),
            'status'                  => $survey->status,
            'link_token'              => $survey->link_token,
            'questions'               => $questions,
        ];
    }

    // ── Store ──────────────────────────────────────────────────
    public function store(int $userId, array $data): Survey
    {
        return DB::transaction(function () use ($userId, $data) {
            $survey = Survey::create([
                'created_by'              => $userId,
                'title_en'                => $data['title_en'],
                'title_ar'                => $data['title_ar'] ?? null,
                'description_en'          => $data['description_en'] ?? null,
                'description_ar'          => $data['description_ar'] ?? null,
                'audience'                => $data['audience'] ?? 'all',
                'hub_id'                  => $data['hub_id'] ?? null,
                'anonymous_responses'     => $data['anonymous_responses'] ?? true,
                'show_results_to_members' => $data['show_results_to_members'] ?? false,
                'closes_at'               => $data['closes_at'] ?? null,
                'status'                  => 'draft',
            ]);

            $this->syncQuestions($survey->id, $data['questions'] ?? []);
            $this->saveToBank($userId, $data['questions'] ?? []);
            $this->incrementBankUsage($data['questions'] ?? []);

            return $survey;
        });
    }

    // ── Update ─────────────────────────────────────────────────
   public function update(Survey $survey, array $data, int $userId): void
    {
        // Guard — once a survey has responses, questions are locked.
        // Editing question IDs would break all existing response analytics.
        if ($survey->response_count > 0) {
            $survey->update([
                'title_en'                => $data['title_en'],
                'title_ar'                => $data['title_ar'] ?? null,
                'description_en'          => $data['description_en'] ?? null,
                'description_ar'          => $data['description_ar'] ?? null,
                'audience'                => $data['audience'] ?? 'all',
                'hub_id'                  => $data['hub_id'] ?? null,
                'anonymous_responses'     => $data['anonymous_responses'] ?? true,
                'show_results_to_members' => $data['show_results_to_members'] ?? false,
                'closes_at'               => $data['closes_at'] ?? null,
            ]);
            return; // questions not touched — responses are safe
        }

        DB::transaction(function () use ($survey, $data, $userId) {
            $survey->update([
                'title_en'                => $data['title_en'],
                'title_ar'                => $data['title_ar'] ?? null,
                'description_en'          => $data['description_en'] ?? null,
                'description_ar'          => $data['description_ar'] ?? null,
                'audience'                => $data['audience'] ?? 'all',
                'hub_id'                  => $data['hub_id'] ?? null,
                'anonymous_responses'     => $data['anonymous_responses'] ?? true,
                'show_results_to_members' => $data['show_results_to_members'] ?? false,
                'closes_at'               => $data['closes_at'] ?? null,
            ]);

            $this->syncQuestions($survey->id, $data['questions'] ?? []);
            $this->saveToBank($userId, $data['questions'] ?? []);
        });
    }

    // ── Publish ────────────────────────────────────────────────
    public function publish(Survey $survey): string
    {
        $token = Str::random(48);
        $survey->update(['status' => 'active', 'link_token' => $token]);
        return $token;
    }

    // ── Close / Reopen ─────────────────────────────────────────
    public function close(Survey $survey): void   { $survey->update(['status' => 'closed']); }
    public function reopen(Survey $survey): void  { $survey->update(['status' => 'active']); }

    // ── Show (take survey page) ────────────────────────────────
    public function show(Survey $survey): array
    {
        $questions = $survey->questions->map(fn($q) => [
            'id'          => $q->id,
            'question_en' => $q->question_en,
            'question_ar' => $q->question_ar,
            'type'        => $q->type,
            'options_en'  => $q->options_en ?? [],
            'options_ar'  => $q->options_ar ?? [],
            'rating_min'  => $q->rating_min,
            'rating_max'  => $q->rating_max,
            'is_required' => $q->is_required,
        ]);

        return [
            'id'                  => $survey->id,
            'title_en'            => $survey->title_en,
            'title_ar'            => $survey->title_ar,
            'description_en'      => $survey->description_en,
            'description_ar'      => $survey->description_ar,
            'anonymous_responses' => $survey->anonymous_responses,
            'status'              => $survey->status,
            'closes_at'           => $survey->closes_at?->toISOString(),
            'questions'           => $questions,
        ];
    }

    // ── Respond ────────────────────────────────────────────────
    public function respond(Survey $survey, array $data, ?int $userId, string $ip): void
    {
        DB::transaction(function () use ($survey, $data, $userId, $ip) {
            $survey->responses()->create([
                'user_id'            => $userId,
                'answers'            => array_intersect_key(
                                        $data['answers'],
                                        array_flip($survey->questions->pluck('id')->toArray())
                        ),
                'submitted_at'       => now(),
                'respondent_name'    => $data['respondent_name'] ?? null,
                'respondent_title'   => $data['respondent_title'] ?? null,
                'respondent_company' => $data['respondent_company'] ?? null,
                'respondent_gender'  => $data['respondent_gender'] ?? null,
                'respondent_age'     => $data['respondent_age'] ?? null,
                'ip_address'         => $ip,
            ]);

            $survey->increment('response_count');

            if ($userId) {
                DB::table('survey_audience')
                    ->where('survey_id', $survey->id)
                    ->where('user_id', $userId)
                    ->update(['has_responded' => true]);
            }
        });
    }

    // ── Results analytics ──────────────────────────────────────
    public function results(Survey $survey): array
    {
        // Load all responses ONCE — passed to both analytics and demographics
        $responses = $survey->responses()->with('user')->get();

        // Pre-decode answers into a keyed collection — no per-question DB query
        $allAnswers = $responses->map(fn ($r) => json_decode($r->answers, true) ?? []);

        $questions = $survey->questions->map(function ($q) use ($allAnswers) {
            return [
                'id'          => $q->id,
                'question_en' => $q->question_en,
                'question_ar' => $q->question_ar,
                'type'        => $q->type,
                'options_en'  => $q->options_en ?? [],
                'options_ar'  => $q->options_ar ?? [],
                'rating_min'  => $q->rating_min,
                'rating_max'  => $q->rating_max,
                'is_required' => $q->is_required,
                'analytics'   => $this->buildAnalyticsFromCollection($q, $allAnswers),
            ];
        });

        $demographics = $this->buildDemographics($responses);
        $surveyData   = $this->formatSurveyForEdit($survey);

        return compact('questions', 'demographics', 'surveyData');
    }

    // ── Private helpers ────────────────────────────────────────
    private function buildAnalyticsFromCollection($q, $allAnswers): array
    {
        $filtered = $allAnswers
            ->filter(fn ($a) => isset($a[$q->id]) && $a[$q->id] !== null && $a[$q->id] !== '');

        $total = $filtered->count();
        if ($total === 0) return ['total' => 0];

        if (in_array($q->type, ['multiple_choice', 'yes_no'])) {
            $counts = $filtered->countBy(fn ($a) => $a[$q->id]);
            return ['total' => $total, 'distribution' => $counts->map(fn ($cnt, $label) => [
                'label' => $label, 'count' => $cnt,
                'pct'   => round($cnt / $total * 100, 1),
            ])->values()->sortByDesc('count')->values()];
        }

        if ($q->type === 'multi_select') {
            $counts = $filtered->flatMap(fn ($a) => (array) $a[$q->id])->countBy();
            return ['total' => $total, 'distribution' => $counts->map(fn ($cnt, $label) => [
                'label' => $label, 'count' => $cnt,
                'pct'   => round($cnt / $total * 100, 1),
            ])->values()->sortByDesc('count')->values()];
        }

        if ($q->type === 'rating') {
            $values = $filtered->map(fn ($a) => (float) $a[$q->id]);
            $dist   = [];
            for ($i = $q->rating_min; $i <= $q->rating_max; $i++) {
                $cnt    = $values->filter(fn ($v) => (int) $v === $i)->count();
                $dist[] = ['label' => $i, 'count' => $cnt, 'pct' => round($cnt / $total * 100, 1)];
            }
            return ['total' => $total, 'avg' => round($values->avg(), 2), 'distribution' => $dist];
        }

        if ($q->type === 'text') {
            return ['total' => $total, 'answers' => $filtered->map(fn ($a) => $a[$q->id])->values()];
        }

        return ['total' => $total];
    }

    private function buildDemographics($responses): array
    {
        $total    = $responses->count();
        $gender   = $responses->groupBy('respondent_gender')->map->count()->toArray();
        $companies = $responses->whereNotNull('respondent_company')
            ->groupBy('respondent_company')->map->count()->sortDesc()->take(10)->toArray();
        $recent   = $responses->sortByDesc('created_at')->take(10)->map(fn($r) => [
            'name'    => $r->display_name ?? ($r->user?->nickname ?? $r->respondent_name ?? 'Anonymous'),
            'company' => $r->respondent_company,
            'title'   => $r->respondent_title,
            'date'    => $r->submitted_at,
        ])->values();
        $ageGroups = $this->buildAgeGroups($responses);
        return compact('total', 'gender', 'companies', 'recent', 'ageGroups');
    }

    private function buildAgeGroups($responses): array
    {
        $groups = ['Under 25' => 0, '25–34' => 0, '35–44' => 0, '45–54' => 0, '55+' => 0, 'Not provided' => 0];
        foreach ($responses as $r) {
            $age = $r->respondent_age ?? null;
            if (!$age)          { $groups['Not provided']++; continue; }
            if ($age < 25)        $groups['Under 25']++;
            elseif ($age < 35)    $groups['25–34']++;
            elseif ($age < 45)    $groups['35–44']++;
            elseif ($age < 55)    $groups['45–54']++;
            else                  $groups['55+']++;
        }
        return $groups;
    }

    private function syncQuestions(int $surveyId, array $questions): void
    {
        DB::table('survey_questions')->where('survey_id', $surveyId)->delete();
        foreach ($questions as $i => $q) {
            DB::table('survey_questions')->insert([
                'survey_id'   => $surveyId,
                'question_en' => $q['question_en'] ?? '',
                'question_ar' => $q['question_ar'] ?? null,
                'type'        => $q['type'],
                'options_en'  => isset($q['options_en']) && count($q['options_en'])
                                    ? json_encode(array_values(array_filter($q['options_en'], fn($o) => trim($o) !== ''))) : null,
                'options_ar'  => isset($q['options_ar']) && count($q['options_ar'])
                                    ? json_encode(array_values(array_filter($q['options_ar'], fn($o) => trim($o) !== ''))) : null,
                'rating_min'  => $q['rating_min'] ?? 1,
                'rating_max'  => $q['rating_max'] ?? 5,
                'is_required' => $q['is_required'] ?? true,
                'sort_order'  => $i,
                'created_at'  => now(),
                'updated_at'  => now(),
            ]);
        }
    }

    private function incrementBankUsage(array $questions): void
    {
        $bankIds = collect($questions)->pluck('bank_item_id')->filter()->unique()->values();
        if ($bankIds->isNotEmpty()) {
            DB::table('question_bank_items')->whereIn('id', $bankIds)->increment('usage_count');
        }
    }

    private function formatCard(Survey $s): array
    {
        return [
            'id'             => $s->id,
            'title_en'       => $s->title_en,
            'title_ar'       => $s->title_ar,
            'description_en' => $s->description_en,
            'status'         => $s->status,
            'audience'       => $s->audience,
            'link_token'     => $s->link_token,
            'response_count' => $s->response_count,
            'question_count' => $s->questions_count,
            'closes_at'      => $s->closes_at?->toISOString(),
            'created_at'     => $s->created_at->toISOString(),
        ];
    }
}