<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Requests\TaskInstances\StoreTaskInstanceRequest;
use App\Http\Requests\TaskInstances\UpdateTaskInstanceRequest;
use App\Models\TaskInstance;
use App\Models\TaskTemplate;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Carbon;

class TaskInstanceController extends Controller
{
    private function clampDayOfMonth(int $year, int $month, int $day): string
    {
        $day = max(1, min(31, $day));
        $last = Carbon::create($year, $month, 1, 0, 0, 0, 'UTC')->endOfMonth()->day;
        $d = min($day, $last);
        return Carbon::create($year, $month, $d, 0, 0, 0, 'UTC')->format('Y-m-d');
    }

    private function weekdayFromIso(string $iso): int
    {
        // 0=Mon .. 6=Sun
        return (int) Carbon::parse($iso, 'UTC')->isoWeekday() - 1;
    }

    private function nextWeekdayOnOrAfter(string $fromIso, int $weekday0Mon): string
    {
        $from = Carbon::parse($fromIso, 'UTC')->startOfDay();
        $target = (($weekday0Mon % 7) + 7) % 7;
        $cur = (int) $from->isoWeekday() - 1;
        $delta = ($target - $cur + 7) % 7;
        return $from->copy()->addDays($delta)->format('Y-m-d');
    }

    private function isoRangeDays(string $fromIso, string $toIso): array
    {
        $from = Carbon::parse($fromIso, 'UTC')->startOfDay();
        $to = Carbon::parse($toIso, 'UTC')->startOfDay();
        if ($to->lt($from)) {
            return [];
        }
        $out = [];
        for ($d = $from->copy(); $d->lte($to); $d->addDay()) {
            $out[] = $d->format('Y-m-d');
        }
        return $out;
    }

    public function generate(Request $request): JsonResponse
    {
        $userId = Auth::id();
        $data = $request->validate([
            'from' => ['required', 'date_format:Y-m-d'],
            'to' => ['required', 'date_format:Y-m-d'],
        ]);

        $fromIso = (string) $data['from'];
        $toIso = (string) $data['to'];
        if (Carbon::parse($toIso, 'UTC')->lt(Carbon::parse($fromIso, 'UTC'))) {
            return $this->error('Invalid range', null, 422);
        }

        $templates = TaskTemplate::query()->where('user_id', $userId)->get();

        $created = 0;
        foreach ($templates as $tpl) {
            $freq = (string) ($tpl->frequency ?? 'daily');
            $anchorDate = $tpl->anchor_date ? Carbon::parse((string) $tpl->anchor_date, 'UTC')->format('Y-m-d') : null;
            $anchorWeekday = $tpl->anchor_weekday !== null ? (int) $tpl->anchor_weekday : null;
            $anchorDom = $tpl->anchor_day_of_month !== null ? (int) $tpl->anchor_day_of_month : null;

            $dueDates = [];

            if ($freq === 'daily') {
                $dueDates = $this->isoRangeDays($fromIso, $toIso);
            } elseif ($freq === 'weekly') {
                $w = $anchorWeekday !== null ? $anchorWeekday : $this->weekdayFromIso($fromIso);
                $d0 = $this->nextWeekdayOnOrAfter($fromIso, $w);
                for ($d = Carbon::parse($d0, 'UTC'); $d->lte(Carbon::parse($toIso, 'UTC')); $d->addDays(7)) {
                    $dueDates[] = $d->format('Y-m-d');
                }
            } elseif ($freq === 'biweekly') {
                $w = $anchorWeekday !== null ? $anchorWeekday : $this->weekdayFromIso($fromIso);
                $base = $anchorDate ?? $this->nextWeekdayOnOrAfter($fromIso, $w);
                $d = Carbon::parse($base, 'UTC')->startOfDay();

                // Move forward in 14-day steps until >= from
                $from = Carbon::parse($fromIso, 'UTC')->startOfDay();
                if ($d->lt($from)) {
                    $days = $d->diffInDays($from);
                    $steps = intdiv((int) $days, 14);
                    $d->addDays($steps * 14);
                    while ($d->lt($from)) {
                        $d->addDays(14);
                    }
                }

                $to = Carbon::parse($toIso, 'UTC')->startOfDay();
                while ($d->lte($to)) {
                    $dueDates[] = $d->format('Y-m-d');
                    $d->addDays(14);
                }
            } elseif ($freq === 'monthly') {
                $dom = $anchorDom !== null ? $anchorDom : (int) Carbon::parse($fromIso, 'UTC')->day;
                $from = Carbon::parse($fromIso, 'UTC');
                $to = Carbon::parse($toIso, 'UTC');
                $cursor = Carbon::create($from->year, $from->month, 1, 0, 0, 0, 'UTC');
                $end = Carbon::create($to->year, $to->month, 1, 0, 0, 0, 'UTC');
                while ($cursor->lte($end)) {
                    $dayIso = $this->clampDayOfMonth((int) $cursor->year, (int) $cursor->month, (int) $dom);
                    if ($dayIso >= $fromIso && $dayIso <= $toIso) {
                        $dueDates[] = $dayIso;
                    }
                    $cursor->addMonthNoOverflow();
                }
            }

            foreach ($dueDates as $due) {
                $instance = TaskInstance::firstOrCreate(
                    ['user_id' => $userId, 'task_template_id' => (int) $tpl->id, 'due_date' => $due],
                    ['status' => 'pending', 'completed_at' => null, 'notes' => null]
                );
                if ($instance->wasRecentlyCreated) {
                    $created++;
                }
            }
        }

        return $this->success(['created' => $created], 'Task instances generated');
    }

    /**
     * Display a listing of the resource.
     */
    public function index(): JsonResponse
    {
        $userId = Auth::id();
        $instances = TaskInstance::query()
            ->where('user_id', $userId)
            ->with('template')
            ->latest()
            ->get();

        return $this->success($instances, 'Task instances list');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(StoreTaskInstanceRequest $request): JsonResponse
    {
        $userId = Auth::id();
        $data = $request->validated();
        $templateId = (int) ($data['task_template_id'] ?? 0);

        $template = TaskTemplate::query()
            ->where('user_id', $userId)
            ->where('id', $templateId)
            ->first();

        if (!$template) {
            return $this->error('Invalid task_template_id', null, 422);
        }

        $data['user_id'] = $userId;
        $instance = TaskInstance::create($data);

        return $this->success($instance->load('template'), 'Task instance created', 201);
    }

    /**
     * Display the specified resource.
     */
    public function show(TaskInstance $taskInstance): JsonResponse
    {
        $userId = Auth::id();
        if ((int) $taskInstance->user_id !== (int) $userId) {
            return $this->error('Not found', null, 404);
        }
        return $this->success($taskInstance->load('template'), 'Task instance details');
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(UpdateTaskInstanceRequest $request, TaskInstance $taskInstance): JsonResponse
    {
        $userId = Auth::id();
        if ((int) $taskInstance->user_id !== (int) $userId) {
            return $this->error('Not found', null, 404);
        }

        $data = $request->validated();
        if (array_key_exists('task_template_id', $data)) {
            $templateId = (int) ($data['task_template_id'] ?? 0);
            $template = TaskTemplate::query()
                ->where('user_id', $userId)
                ->where('id', $templateId)
                ->first();
            if (!$template) {
                return $this->error('Invalid task_template_id', null, 422);
            }
        }

        $taskInstance->update($data);

        return $this->success($taskInstance->load('template'), 'Task instance updated');
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(TaskInstance $taskInstance): JsonResponse
    {
        $userId = Auth::id();
        if ((int) $taskInstance->user_id !== (int) $userId) {
            return $this->error('Not found', null, 404);
        }
        $taskInstance->delete();

        return $this->success(null, 'Task instance deleted');
    }
}
