using Microsoft.EntityFrameworkCore;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Validation;

namespace ZB.MOM.WW.OtOpcUa.Admin.Services;

/// <summary>
///     Equipment CRUD scoped to a generation. The Admin app writes against Draft generations only;
///     Published generations are read-only (to create changes, clone to a new draft via
///     <see cref="GenerationService.CreateDraftAsync"/>).
/// </summary>
public sealed class EquipmentService(OtOpcUaConfigDbContext db)
{
    public Task<List<Equipment>> ListAsync(long generationId, CancellationToken ct) =>
        db.Equipment.AsNoTracking()
            .Where(e => e.GenerationId == generationId)
            .OrderBy(e => e.Name)
            .ToListAsync(ct);

    public Task<Equipment?> FindAsync(long generationId, string equipmentId, CancellationToken ct) =>
        db.Equipment.AsNoTracking()
            .FirstOrDefaultAsync(e => e.GenerationId == generationId && e.EquipmentId == equipmentId, ct);

    /// <summary>
    ///     Creates a new equipment row in the given draft. The EquipmentId is auto-derived from
    ///     a fresh EquipmentUuid per decision #125; operator-supplied IDs are rejected upstream.
    /// </summary>
    public async Task<Equipment> CreateAsync(long draftId, Equipment input, CancellationToken ct)
    {
        input.GenerationId = draftId;
        input.EquipmentUuid = input.EquipmentUuid == Guid.Empty ? Guid.NewGuid() : input.EquipmentUuid;
        input.EquipmentId = DraftValidator.DeriveEquipmentId(input.EquipmentUuid);
        db.Equipment.Add(input);
        await db.SaveChangesAsync(ct);
        return input;
    }

    public async Task UpdateAsync(Equipment updated, CancellationToken ct)
    {
        // Only editable fields are persisted; EquipmentId + EquipmentUuid are immutable once set.
        var existing = await db.Equipment
            .FirstOrDefaultAsync(e => e.EquipmentRowId == updated.EquipmentRowId, ct)
            ?? throw new InvalidOperationException($"Equipment row {updated.EquipmentRowId} not found");

        existing.Name = updated.Name;
        existing.MachineCode = updated.MachineCode;
        existing.ZTag = updated.ZTag;
        existing.SAPID = updated.SAPID;
        existing.Manufacturer = updated.Manufacturer;
        existing.Model = updated.Model;
        existing.SerialNumber = updated.SerialNumber;
        existing.HardwareRevision = updated.HardwareRevision;
        existing.SoftwareRevision = updated.SoftwareRevision;
        existing.YearOfConstruction = updated.YearOfConstruction;
        existing.AssetLocation = updated.AssetLocation;
        existing.ManufacturerUri = updated.ManufacturerUri;
        existing.DeviceManualUri = updated.DeviceManualUri;
        existing.DriverInstanceId = updated.DriverInstanceId;
        existing.DeviceId = updated.DeviceId;
        existing.UnsLineId = updated.UnsLineId;
        existing.EquipmentClassRef = updated.EquipmentClassRef;
        existing.Enabled = updated.Enabled;

        await db.SaveChangesAsync(ct);
    }

    public async Task DeleteAsync(Guid equipmentRowId, CancellationToken ct)
    {
        var row = await db.Equipment.FirstOrDefaultAsync(e => e.EquipmentRowId == equipmentRowId, ct);
        if (row is null) return;
        db.Equipment.Remove(row);
        await db.SaveChangesAsync(ct);
    }
}
