using Microsoft.EntityFrameworkCore;

namespace Nexus.Api.Data;

public sealed class NexusDbContext(DbContextOptions<NexusDbContext> options) : DbContext(options)
{
    public DbSet<Project> Projects => Set<Project>();
    public DbSet<WorkTask> Tasks => Set<WorkTask>();
    public DbSet<Notification> Notifications => Set<Notification>();
    public DbSet<ActivityEvent> Activity => Set<ActivityEvent>();
    public DbSet<NexusUser> Users => Set<NexusUser>();
    public DbSet<RefreshToken> RefreshTokens => Set<RefreshToken>();
    public DbSet<SeedAudit> SeedAudits => Set<SeedAudit>();
    public DbSet<OpenClawRun> OpenClawRuns => Set<OpenClawRun>();
    public DbSet<OpenClawRunHistory> OpenClawRunHistory => Set<OpenClawRunHistory>();
    public DbSet<OpenClawConnectionProfile> OpenClawConnectionProfiles =>
        Set<OpenClawConnectionProfile>();
    public DbSet<AgentProposal> AgentProposals => Set<AgentProposal>();
    public DbSet<AgentProvisionRequest> AgentProvisionRequests =>
        Set<AgentProvisionRequest>();
    public DbSet<OperationClaim> OperationClaims => Set<OperationClaim>();
    public DbSet<OutboxEvent> OutboxEvents => Set<OutboxEvent>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new OpenClawConnectionProfileConfiguration());
        ConfigureAgentProvisioning(modelBuilder);
        modelBuilder.Entity<Project>().Property(x => x.Name).HasMaxLength(160);
        modelBuilder.Entity<WorkTask>(entity =>
        {
            entity.Property(x => x.Title).HasMaxLength(240);
            entity.Property(x => x.Detail).HasMaxLength(2000);
            entity.Property(x => x.Source).HasMaxLength(60);
            entity.Property(x => x.AssignedTo).HasMaxLength(60);
            entity.Property(x => x.ExpectedFrom).HasMaxLength(60);
            entity.HasIndex(x => x.Source);
            entity.HasIndex(x => x.AssignedTo);
            entity.HasIndex(x => x.IsAgentTask);
            entity.HasIndex(x => x.ExpectedFrom);
            entity.HasIndex(x => new { x.State, x.UpdatedAt, x.Id })
                .IsDescending(false, true, false)
                .HasDatabaseName("IX_Tasks_State_UpdatedAt_Id_Board");
            entity.HasIndex(x => new { x.UpdatedAt, x.Id })
                .IsDescending(true, true)
                .HasFilter("\"State\" = 'Done'")
                .HasDatabaseName("IX_Tasks_Done_UpdatedAt_Id");
            entity.HasIndex(x => new { x.ParentTaskId, x.State })
                .HasFilter("\"ParentTaskId\" IS NOT NULL")
                .HasDatabaseName("IX_Tasks_ParentTaskId_State");
            entity.HasIndex(x => new { x.ExpectedFrom, x.State, x.UpdatedAt, x.Id })
                .IsDescending(false, false, true, false)
                .HasFilter("\"IsAgentTask\" = TRUE")
                .HasDatabaseName("IX_Tasks_AgentWorkflow");
            entity.HasOne(x => x.ParentTask)
                  .WithMany(x => x.ChildTasks)
                  .HasForeignKey(x => x.ParentTaskId)
                  .OnDelete(DeleteBehavior.SetNull);
        });
        modelBuilder.Entity<Notification>(entity =>
        {
            entity.Property(x => x.Title).HasMaxLength(240);
            entity.Property(x => x.Message).HasMaxLength(1000);
            entity.Property(x => x.Type).HasMaxLength(60);
            entity.Property(x => x.ForUser).HasMaxLength(60);
            entity.HasIndex(x => new { x.ForUser, x.IsRead, x.CreatedAt });
        });

        modelBuilder.Entity<ActivityEvent>(entity =>
        {
            entity.Property(x => x.Message).HasMaxLength(1000);
            entity.HasIndex(x => x.TaskId);
            entity.HasIndex(x => new { x.TaskId, x.CreatedAt, x.Id })
                .IsDescending(false, true, true)
                .HasFilter("\"TaskId\" IS NOT NULL")
                .HasDatabaseName("IX_Activity_TaskId_CreatedAt_Id");
        });
        modelBuilder.Entity<NexusUser>().HasIndex(u => u.NormalizedEmail).IsUnique();
        modelBuilder.Entity<RefreshToken>().HasIndex(r => r.TokenHash).IsUnique();
        modelBuilder.Entity<RefreshToken>().HasIndex(r => new { r.UserId, r.FamilyId });
        modelBuilder.Entity<RefreshToken>().Property(r => r.ConcurrencyStamp).IsConcurrencyToken();
        modelBuilder.Entity<RefreshToken>()
            .HasOne(r => r.User)
            .WithMany(u => u.RefreshTokens)
            .HasForeignKey(r => r.UserId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<ActivityEvent>().HasIndex(x => x.CreatedAt);

        modelBuilder.Entity<OpenClawRun>(entity =>
        {
            entity.Property(x => x.Title).HasMaxLength(160);
            entity.Property(x => x.Prompt).HasColumnType("text");
            entity.Property(x => x.AgentId).HasMaxLength(200);
            entity.Property(x => x.SessionKey).HasMaxLength(500);
            entity.Property(x => x.Status).HasMaxLength(40);
            entity.Property(x => x.OpenClawRunId).HasMaxLength(240);
            entity.Property(x => x.StartIdempotencyKey).HasMaxLength(200);
            entity.Property(x => x.CorrelationId).HasMaxLength(200);
            entity.Property(x => x.Actor).HasMaxLength(240);
            entity.Property(x => x.TraceParent).HasMaxLength(128);
            entity.Property(x => x.LastError).HasMaxLength(2000);
            entity.Property(x => x.Revision).IsConcurrencyToken();
            entity.HasIndex(x => x.StartIdempotencyKey).IsUnique();
            entity.HasIndex(x => x.OpenClawRunId).IsUnique();
            entity.HasIndex(x => new { x.Status, x.UpdatedAt });
            entity.HasIndex(x => x.TaskId);
            entity.HasIndex(x => x.ProjectId);
            entity.HasIndex(x => x.SessionKey);
            entity.HasIndex(x => x.RetriedFromRunId);
            entity.HasOne<WorkTask>()
                .WithMany()
                .HasForeignKey(x => x.TaskId)
                .OnDelete(DeleteBehavior.SetNull);
            entity.HasOne<Project>()
                .WithMany()
                .HasForeignKey(x => x.ProjectId)
                .OnDelete(DeleteBehavior.SetNull);
        });

        modelBuilder.Entity<OpenClawRunHistory>(entity =>
        {
            entity.Property(x => x.Action).HasMaxLength(80);
            entity.Property(x => x.FromStatus).HasMaxLength(40);
            entity.Property(x => x.ToStatus).HasMaxLength(40);
            entity.Property(x => x.Message).HasMaxLength(2000);
            entity.Property(x => x.Actor).HasMaxLength(240);
            entity.Property(x => x.CorrelationId).HasMaxLength(200);
            entity.Property(x => x.IdempotencyKey).HasMaxLength(200);
            entity.Property(x => x.TraceParent).HasMaxLength(128);
            entity.Property(x => x.GatewayEventId).HasMaxLength(120);
            entity.HasIndex(x => new { x.RunId, x.OccurredAt });
            entity.HasIndex(x => new { x.RunId, x.Action, x.IdempotencyKey }).IsUnique();
            entity.HasIndex(x => x.GatewayEventId).IsUnique();
            entity.HasOne(x => x.Run)
                .WithMany(x => x.History)
                .HasForeignKey(x => x.RunId)
                .OnDelete(DeleteBehavior.Cascade);
        });
    }

    private static void ConfigureAgentProvisioning(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AgentProposal>(entity =>
        {
            entity.Property(x => x.Source).HasMaxLength(40);
            entity.Property(x => x.RequestedName).HasMaxLength(80);
            entity.Property(x => x.RequestedAgentId).HasMaxLength(64);
            entity.Property(x => x.Role).HasMaxLength(160);
            entity.Property(x => x.Description).HasMaxLength(4000);
            entity.Property(x => x.Model).HasMaxLength(240);
            entity.Property(x => x.Emoji).HasMaxLength(32);
            entity.Property(x => x.Avatar).HasMaxLength(2048);
            entity.Property(x => x.Workspace).HasMaxLength(2048);
            entity.Property(x => x.StandardFilesJson).HasColumnType("jsonb");
            entity.Property(x => x.StandardFilesHash).HasMaxLength(64);
            entity.Property(x => x.Status).HasMaxLength(40);
            entity.Property(x => x.RequestedBy).HasMaxLength(240);
            entity.Property(x => x.ApprovedBy).HasMaxLength(240);
            entity.Property(x => x.RejectedBy).HasMaxLength(240);
            entity.Property(x => x.RejectionReason).HasMaxLength(1000);
            entity.Property(x => x.OpenClawAgentId).HasMaxLength(128);
            entity.Property(x => x.OpenClawWorkspace).HasMaxLength(2048);
            entity.Property(x => x.LastErrorCode).HasMaxLength(120);
            entity.Property(x => x.LastErrorMessage).HasMaxLength(2000);
            entity.Property(x => x.Revision).IsConcurrencyToken();
            entity.HasIndex(x => new { x.Status, x.UpdatedAt });
            entity.HasIndex(x => x.RequestedAgentId);
            entity.HasIndex(x => x.OpenClawAgentId);
        });

        modelBuilder.Entity<AgentProvisionRequest>(entity =>
        {
            entity.Property(x => x.Stage).HasMaxLength(40);
            entity.Property(x => x.Status).HasMaxLength(40);
            entity.Property(x => x.IdempotencyKeyHash).HasMaxLength(64);
            entity.Property(x => x.Actor).HasMaxLength(240);
            entity.Property(x => x.CorrelationId).HasMaxLength(200);
            entity.Property(x => x.TraceParent).HasMaxLength(128);
            entity.Property(x => x.OpenClawAgentId).HasMaxLength(128);
            entity.Property(x => x.LastErrorCode).HasMaxLength(120);
            entity.Property(x => x.LastErrorMessage).HasMaxLength(2000);
            entity.Property(x => x.LeaseOwner).HasMaxLength(160);
            entity.Property(x => x.Revision).IsConcurrencyToken();
            entity.HasIndex(x => new { x.ProposalId, x.Attempt }).IsUnique();
            entity.HasIndex(x => new { x.Status, x.CreatedAt });
            entity.HasIndex(x => x.LeaseUntil);
            entity.HasOne(x => x.Proposal)
                .WithMany(x => x.ProvisionRequests)
                .HasForeignKey(x => x.ProposalId)
                .OnDelete(DeleteBehavior.Cascade);
        });

        modelBuilder.Entity<OperationClaim>(entity =>
        {
            entity.Property(x => x.Operation).HasMaxLength(100);
            entity.Property(x => x.IdempotencyKeyHash).HasMaxLength(64);
            entity.Property(x => x.RequestHash).HasMaxLength(64);
            entity.Property(x => x.State).HasMaxLength(40);
            entity.Property(x => x.ResultCode).HasMaxLength(120);
            entity.HasIndex(x => new { x.Operation, x.IdempotencyKeyHash }).IsUnique();
            entity.HasIndex(x => x.ExpiresAt);
        });

        modelBuilder.Entity<OutboxEvent>(entity =>
        {
            entity.HasKey(x => x.Sequence);
            entity.Property(x => x.Sequence).ValueGeneratedOnAdd();
            entity.Property(x => x.Type).HasMaxLength(120);
            entity.Property(x => x.AggregateType).HasMaxLength(80);
            entity.Property(x => x.AggregateId).HasMaxLength(128);
            entity.Property(x => x.PayloadJson).HasColumnType("jsonb");
            entity.Property(x => x.LastErrorCode).HasMaxLength(120);
            entity.HasIndex(x => x.EventId).IsUnique();
            entity.HasIndex(x => new { x.PublishedAt, x.Sequence });
            entity.HasIndex(x => x.OccurredAt);
        });
    }
}
