<?php

namespace App\Contracts;

use App\Models\GoLiveTask;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Mtc\MercuryDataModels\Tenant;

abstract class GoLiveChecklist
{
    public const DOMAINS = 'domains';
    public const FRONTEND = 'frontend';
    public const DEVELOPER = 'developer';
    public const MARKETING = 'marketing';
    public const CONFIGURATION = 'configuration';

    protected static array $tasks;

    public function isApplicable(Tenant $site): bool
    {
        return true;
    }

    abstract public function key(): string;
    abstract public function category(): string;

    protected string $failure_reason = '';

    abstract public function handle(): bool;

    public function name(): string
    {
        return __('go-live.names.' . $this->key());
    }

    public function description(): string
    {
        return __('go-live.descriptions.' . $this->key());
    }

    public function getFailureReason(): string
    {
        return $this->failure_reason;
    }

    public function requiresManualConfirm(): bool
    {
        return false;
    }

    public function start(bool $manual_call = false): void
    {
        $task = $this->trackStart();
        try {
            $details = $task->details ?? [];
            $manual_call_for_manual_task = $manual_call && $this->requiresManualConfirm();
            if ($this->handle() || $manual_call_for_manual_task) {
                $details[] = Carbon::now()->format('Y-m-d H:i') . ' - [Completed] by ' . Auth::user()->name;
                $task->update([
                    'completed_at' => Carbon::now(),
                    'completed_by' => Auth::id(),
                    'details' => $details,
                ]);
            } else {
                $details[] = Carbon::now()->format('Y-m-d H:i') . ' - [Failed] ' . $this->failure_reason;
                $task->update([
                    'failed_at' => Carbon::now(),
                    'details' => $details,
                ]);
            }
        } catch (\Throwable $throwable) {
            $details = $task->details ?? [];
            $details[] = Carbon::now()->format('Y-m-d H:i') . ' - [ERROR] ' . $throwable->getMessage();
            $task->update([
                'failed_at' => Carbon::now(),
                'details' => $details,
            ]);
        }
    }

    protected function trackStart(): GoLiveTask
    {
        return GoLiveTask::query()
            ->updateOrCreate([
                'tenant_id' => \tenant('id'),
                'task' => $this->key(),
            ], [
                'completed_at' => null,
                'failed_at' => null,
            ]);
    }


    public function status(): GoLiveStatus
    {
        $this->loadTasks();
        $task = self::$tasks[\tenant('id')][$this->key()] ?? null;

        if (!$this->isApplicable(\tenant())) {
            return GoLiveStatus::NOT_APPLICABLE;
        }

        if (!$task) {
            return GoLiveStatus::PENDING;
        }

        if ($task->completed_at) {
            return GoLiveStatus::COMPLETED;
        }

        return $task->failed_at
            ? GoLiveStatus::FAILED
            : GoLiveStatus::RUNNING;
    }

    public function statusIcon(): string
    {
        return match ($this->status()) {
            GoLiveStatus::COMPLETED => 'check',
            GoLiveStatus::FAILED => 'triangle-exclamation',
            GoLiveStatus::RUNNING => 'spinner-third',
            GoLiveStatus::PENDING => 'timer',
            GoLiveStatus::NOT_APPLICABLE => 'xmark',
        };
    }

    private function loadTasks(): void
    {
        if (!isset(self::$tasks[\tenant('id')])) {
            self::$tasks[\tenant('id')] = GoLiveTask::query()
                ->where('tenant_id', \tenant('id'))
                ->get()
                ->keyBy('task');
        }
    }

    public function details(): array
    {
        $this->loadTasks();
        $task = self::$tasks[\tenant('id')][$this->key()] ?? null;
        $details = $task?->details ?? [];
        array_unshift($details, $this->description());
        return $details;
    }
}
