using ClaudeDo.Data.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Data.Configuration;

public class TaskEntityConfiguration : IEntityTypeConfiguration<TaskEntity>
{
    private static string StatusToString(TaskStatus v)
        => v switch
        {
            TaskStatus.Idle             => "idle",
            TaskStatus.Queued           => "queued",
            TaskStatus.Running          => "running",
            TaskStatus.WaitingForReview    => "waiting_for_review",
            TaskStatus.WaitingForChildren  => "waiting_for_children",
            TaskStatus.Done                => "done",
            TaskStatus.Failed           => "failed",
            TaskStatus.Cancelled        => "cancelled",
            _ => throw new ArgumentOutOfRangeException(nameof(v)),
        };

    private static TaskStatus StatusFromString(string v)
        => v switch
        {
            "idle"               => TaskStatus.Idle,
            "queued"             => TaskStatus.Queued,
            "running"            => TaskStatus.Running,
            "waiting_for_review"    => TaskStatus.WaitingForReview,
            "waiting_for_children"  => TaskStatus.WaitingForChildren,
            "done"                  => TaskStatus.Done,
            "failed"             => TaskStatus.Failed,
            "cancelled"          => TaskStatus.Cancelled,
            _ => throw new ArgumentOutOfRangeException(nameof(v)),
        };

    private static readonly ValueConverter<TaskStatus, string> StatusConverter =
        new(v => StatusToString(v), v => StatusFromString(v));

    private static string PhaseToString(PlanningPhase v)
        => v switch
        {
            PlanningPhase.None      => "none",
            PlanningPhase.Active    => "active",
            PlanningPhase.Finalized => "finalized",
            _ => throw new ArgumentOutOfRangeException(nameof(v)),
        };

    private static PlanningPhase PhaseFromString(string v)
        => v switch
        {
            "none"      => PlanningPhase.None,
            "active"    => PlanningPhase.Active,
            "finalized" => PlanningPhase.Finalized,
            _ => throw new ArgumentOutOfRangeException(nameof(v)),
        };

    private static readonly ValueConverter<PlanningPhase, string> PhaseConverter =
        new(v => PhaseToString(v), v => PhaseFromString(v));

    public void Configure(EntityTypeBuilder<TaskEntity> builder)
    {
        builder.ToTable("tasks");

        builder.HasKey(t => t.Id);
        builder.Property(t => t.Id).HasColumnName("id");
        builder.Property(t => t.Number).HasColumnName("number").IsRequired();
        builder.Property(t => t.ListId).HasColumnName("list_id").IsRequired();
        builder.Property(t => t.Title).HasColumnName("title").IsRequired();
        builder.Property(t => t.Description).HasColumnName("description");
        builder.Property(t => t.Status).HasColumnName("status").IsRequired()
            .HasConversion(StatusConverter);
        builder.Property(t => t.PlanningPhase).HasColumnName("planning_phase").IsRequired()
            .HasConversion(PhaseConverter).HasDefaultValue(PlanningPhase.None);
        builder.Property(t => t.BlockedByTaskId).HasColumnName("blocked_by_task_id");
        builder.Property(t => t.DependsOnTaskId).HasColumnName("depends_on_task_id");
        builder.Property(t => t.ScheduledFor).HasColumnName("scheduled_for");
        builder.Property(t => t.Result).HasColumnName("result");
        builder.Property(t => t.ReviewFeedback).HasColumnName("review_feedback");
        builder.Property(t => t.RoadblockCount).HasColumnName("roadblock_count").HasDefaultValue(0);
        builder.Property(t => t.FailureReason).HasColumnName("failure_reason");
        builder.Property(t => t.FailureTurnsUsed).HasColumnName("failure_turns_used");
        builder.Property(t => t.FailureMaxTurns).HasColumnName("failure_max_turns");
        builder.Property(t => t.UsageLimitAutoContinuedAt).HasColumnName("usage_limit_auto_continued_at");
        builder.Property(t => t.LogPath).HasColumnName("log_path");
        builder.Property(t => t.CreatedAt).HasColumnName("created_at").IsRequired();
        builder.Property(t => t.StartedAt).HasColumnName("started_at");
        builder.Property(t => t.FinishedAt).HasColumnName("finished_at");
        builder.Property(t => t.CommitType).HasColumnName("commit_type").IsRequired().HasDefaultValue("chore");
        builder.Property(t => t.Model).HasColumnName("model");
        builder.Property(t => t.SystemPrompt).HasColumnName("system_prompt");
        builder.Property(t => t.AgentPath).HasColumnName("agent_path");
        builder.Property(t => t.MaxTurns).HasColumnName("max_turns");
        builder.Property(t => t.PermissionMode).HasColumnName("permission_mode");
        builder.Property(t => t.IsStarred).HasColumnName("is_starred").HasDefaultValue(false);
        builder.Property(t => t.IsMyDay).HasColumnName("is_my_day").HasDefaultValue(false);
        builder.Property(t => t.IsManual).HasColumnName("is_manual").HasDefaultValue(false);
        builder.Property(t => t.Notes).HasColumnName("notes");
        builder.Property(t => t.SortOrder).HasColumnName("sort_order").IsRequired().HasDefaultValue(0);
        builder.Property(t => t.SessionSkills).HasColumnName("session_skills");
        builder.Property(t => t.HandlerBaseCommit).HasColumnName("handler_base_commit");
        builder.Property(t => t.HandlerHeadCommit).HasColumnName("handler_head_commit");
        builder.Property(t => t.InteractiveSessionId).HasColumnName("interactive_session_id");
        builder.Property(t => t.TicketRef).HasColumnName("ticket_ref");

        builder.Property(t => t.ParentTaskId).HasColumnName("parent_task_id");
        builder.Property(t => t.PlanningSessionId).HasColumnName("planning_session_id");
        builder.Property(t => t.PlanningSessionToken).HasColumnName("planning_session_token");
        builder.Property(t => t.PlanningFinalizedAt).HasColumnName("planning_finalized_at");

        builder.Property(t => t.CreatedBy).HasColumnName("created_by");

        builder.HasOne(t => t.Parent)
            .WithMany(t => t.Children)
            .HasForeignKey(t => t.ParentTaskId)
            .OnDelete(DeleteBehavior.Restrict);

        // BlockedBy: predecessor in a sequential chain. SetNull on delete so child becomes pickable.
        builder.HasOne<TaskEntity>()
            .WithMany()
            .HasForeignKey(t => t.BlockedByTaskId)
            .OnDelete(DeleteBehavior.SetNull);

        // DependsOn: user/MCP-declared predecessor. SetNull on delete so the dependent becomes
        // pickable rather than blocked forever on a task that no longer exists.
        builder.HasOne<TaskEntity>()
            .WithMany()
            .HasForeignKey(t => t.DependsOnTaskId)
            .OnDelete(DeleteBehavior.SetNull);

        builder.HasOne(t => t.List)
            .WithMany(l => l.Tasks)
            .HasForeignKey(t => t.ListId)
            .OnDelete(DeleteBehavior.Cascade);

        builder.HasOne(t => t.Worktree)
            .WithOne(w => w.Task)
            .HasForeignKey<WorktreeEntity>(w => w.TaskId);

        builder.HasIndex(t => t.Number).IsUnique().HasDatabaseName("idx_tasks_number");
        builder.HasIndex(t => t.ListId).HasDatabaseName("idx_tasks_list_id");
        builder.HasIndex(t => t.Status).HasDatabaseName("idx_tasks_status");
        builder.HasIndex(t => new { t.ListId, t.SortOrder }).HasDatabaseName("idx_tasks_list_sort");
        builder.HasIndex(t => t.ParentTaskId).HasDatabaseName("idx_tasks_parent_task_id");
        builder.HasIndex(t => t.BlockedByTaskId).HasDatabaseName("idx_tasks_blocked_by");
        builder.HasIndex(t => t.DependsOnTaskId).HasDatabaseName("idx_tasks_depends_on");
    }
}
