using Microsoft.EntityFrameworkCore;
using Nexus.Api.Data;

namespace Nexus.Api.Repositories;

public sealed class OpenClawConnectionProfileRepository(NexusDbContext db)
    : IOpenClawConnectionProfileRepository
{
    public Task<OpenClawConnectionProfile?> GetPrimaryAsync(
        CancellationToken cancellationToken = default)
        => db.Set<OpenClawConnectionProfile>()
            .AsNoTracking()
            .SingleOrDefaultAsync(
                profile => profile.ProfileId == OpenClawConnectionProfile.PrimaryProfileId,
                cancellationToken);

    public async Task<OpenClawConnectionProfile> SavePrimaryAsync(
        OpenClawConnectionProfile profile,
        int? expectedRevision,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(profile);
        if (!string.Equals(
                profile.ProfileId,
                OpenClawConnectionProfile.PrimaryProfileId,
                StringComparison.Ordinal))
        {
            throw new ArgumentException("Only the primary OpenClaw profile is supported.", nameof(profile));
        }

        var profiles = db.Set<OpenClawConnectionProfile>();
        var current = await profiles.SingleOrDefaultAsync(
            item => item.ProfileId == OpenClawConnectionProfile.PrimaryProfileId,
            cancellationToken);

        if (current is null)
        {
            if (expectedRevision is not null and not 0)
            {
                throw new OpenClawConnectionProfileConcurrencyException(
                    "The OpenClaw connection profile no longer exists.");
            }

            profile.Revision = 1;
            profile.CreatedAt = profile.CreatedAt == default
                ? DateTimeOffset.UtcNow
                : profile.CreatedAt;
            profile.UpdatedAt = DateTimeOffset.UtcNow;
            profiles.Add(profile);
            await SaveChangesAsync(cancellationToken);
            return Clone(profile);
        }

        if (expectedRevision is null || expectedRevision.Value != current.Revision)
        {
            throw new OpenClawConnectionProfileConcurrencyException(
                "The OpenClaw connection profile changed since it was loaded.",
                current.Revision);
        }

        current.Endpoint = profile.Endpoint;
        current.DiscoverySource = profile.DiscoverySource;
        current.RequiredVersion = profile.RequiredVersion;
        current.TlsCertificateFingerprint = profile.TlsCertificateFingerprint;
        current.AdoptionState = profile.AdoptionState;
        current.ManagementEnabled = profile.ManagementEnabled;
        current.CapabilityHash = profile.CapabilityHash;
        current.DeviceId = profile.DeviceId;
        current.LastProbedAt = profile.LastProbedAt;
        current.LastVerifiedAt = profile.LastVerifiedAt;
        current.AdoptedAt = profile.AdoptedAt;
        current.UpdatedAt = DateTimeOffset.UtcNow;
        current.Revision++;

        await SaveChangesAsync(cancellationToken);
        return Clone(current);
    }

    public async Task<bool> DeletePrimaryAsync(
        int expectedRevision,
        CancellationToken cancellationToken = default)
    {
        var profiles = db.Set<OpenClawConnectionProfile>();
        var current = await profiles.SingleOrDefaultAsync(
            item => item.ProfileId == OpenClawConnectionProfile.PrimaryProfileId,
            cancellationToken);
        if (current is null)
            return false;
        if (current.Revision != expectedRevision)
        {
            throw new OpenClawConnectionProfileConcurrencyException(
                "The OpenClaw connection profile changed since it was loaded.",
                current.Revision);
        }

        profiles.Remove(current);
        await SaveChangesAsync(cancellationToken);
        return true;
    }

    private async Task SaveChangesAsync(CancellationToken cancellationToken)
    {
        try
        {
            await db.SaveChangesAsync(cancellationToken);
        }
        catch (DbUpdateConcurrencyException exception)
        {
            throw new OpenClawConnectionProfileConcurrencyException(
                "The OpenClaw connection profile changed concurrently.",
                innerException: exception);
        }
    }

    private static OpenClawConnectionProfile Clone(OpenClawConnectionProfile profile)
        => new()
        {
            ProfileId = profile.ProfileId,
            Endpoint = profile.Endpoint,
            DiscoverySource = profile.DiscoverySource,
            RequiredVersion = profile.RequiredVersion,
            TlsCertificateFingerprint = profile.TlsCertificateFingerprint,
            AdoptionState = profile.AdoptionState,
            ManagementEnabled = profile.ManagementEnabled,
            CapabilityHash = profile.CapabilityHash,
            DeviceId = profile.DeviceId,
            Revision = profile.Revision,
            CreatedAt = profile.CreatedAt,
            UpdatedAt = profile.UpdatedAt,
            LastProbedAt = profile.LastProbedAt,
            LastVerifiedAt = profile.LastVerifiedAt,
            AdoptedAt = profile.AdoptedAt
        };
}
