<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Collection;
use App\Models\User;
use App\Services\CollectionHealthService;
use App\Services\CollectionHistoryService;
use App\Services\CollectionQualityService;
use Illuminate\Validation\ValidationException;

class CollectionWorkflowService
{
    public function __construct(
        private readonly CollectionHealthService $health,
    ) {
    }

    public function update(Collection $collection, array $attributes, ?User $actor = null): Collection
    {
        $nextWorkflow = isset($attributes['workflow_state']) ? (string) $attributes['workflow_state'] : (string) $collection->workflow_state;
        $this->assertTransition((string) $collection->workflow_state, $nextWorkflow);

        $before = [
            'workflow_state' => $collection->workflow_state,
            'program_key' => $collection->program_key,
            'partner_key' => $collection->partner_key,
            'experiment_key' => $collection->experiment_key,
            'placement_eligibility' => (bool) $collection->placement_eligibility,
        ];

        $collection->forceFill([
            'workflow_state' => $nextWorkflow !== '' ? $nextWorkflow : null,
            'program_key' => array_key_exists('program_key', $attributes) ? ($attributes['program_key'] ?: null) : $collection->program_key,
            'partner_key' => array_key_exists('partner_key', $attributes) ? ($attributes['partner_key'] ?: null) : $collection->partner_key,
            'experiment_key' => array_key_exists('experiment_key', $attributes) ? ($attributes['experiment_key'] ?: null) : $collection->experiment_key,
            'placement_eligibility' => array_key_exists('placement_eligibility', $attributes) ? (bool) $attributes['placement_eligibility'] : $collection->placement_eligibility,
        ])->save();

        $fresh = $this->health->refresh($collection->fresh(), $actor, 'workflow');

        app(CollectionHistoryService::class)->record(
            $fresh,
            $actor,
            'workflow_updated',
            'Collection workflow updated.',
            $before,
            [
                'workflow_state' => $fresh->workflow_state,
                'program_key' => $fresh->program_key,
                'partner_key' => $fresh->partner_key,
                'experiment_key' => $fresh->experiment_key,
                'placement_eligibility' => (bool) $fresh->placement_eligibility,
            ]
        );

        return $fresh;
    }

    public function qualityRefresh(Collection $collection, ?User $actor = null): Collection
    {
        $collection = app(CollectionQualityService::class)->sync($collection->fresh());
        $collection = $this->health->refresh($collection, $actor, 'quality-refresh');
        $collection = app(CollectionRankingService::class)->refresh($collection);

        app(CollectionHistoryService::class)->record($collection, $actor, 'quality_refreshed', 'Collection quality and ranking refreshed.');

        return $collection;
    }

    private function assertTransition(string $current, string $next): void
    {
        if ($next === '' || $next === $current) {
            return;
        }

        $allowed = [
            '' => [Collection::WORKFLOW_DRAFT, Collection::WORKFLOW_APPROVED],
            Collection::WORKFLOW_DRAFT => [Collection::WORKFLOW_IN_REVIEW, Collection::WORKFLOW_APPROVED],
            Collection::WORKFLOW_IN_REVIEW => [Collection::WORKFLOW_DRAFT, Collection::WORKFLOW_APPROVED],
            Collection::WORKFLOW_APPROVED => [Collection::WORKFLOW_PROGRAMMED, Collection::WORKFLOW_ARCHIVED, Collection::WORKFLOW_IN_REVIEW],
            Collection::WORKFLOW_PROGRAMMED => [Collection::WORKFLOW_APPROVED, Collection::WORKFLOW_ARCHIVED],
            Collection::WORKFLOW_ARCHIVED => [Collection::WORKFLOW_APPROVED],
        ];

        if (! in_array($next, $allowed[$current] ?? [], true)) {
            throw ValidationException::withMessages([
                'workflow_state' => 'This workflow transition is not allowed.',
            ]);
        }
    }
}