using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using SteUp.Shared.Core.Entities;
using SteUp.Shared.Core.Enum;
using SteUp.Shared.Core.Helpers;
using SteUp.Shared.Core.Interface.LocalDb;

namespace SteUp.Data.LocalDb.EntityServices;

public class IspezioniService(AppDbContext db) : IIspezioniService
{
    public Task<Ispezione?> GetIspezioneAsync(string codMdep, DateTime data, string rilevatore) =>
        db.Ispezioni
            .Include(x => x.Schede)
            .ThenInclude(s => s.Articoli)
            .FirstOrDefaultAsync(x =>
                x.CodMdep == codMdep &&
                x.Data == data &&
                x.Rilevatore == rilevatore);

    public Task<List<Ispezione>> GetAllIspezioni(string rilevatore) =>
        db.Ispezioni
            .Where(x => x.Rilevatore.ToLower() == rilevatore.ToLower())
            .AsNoTracking()
            .ToListAsync();

    public Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync(string rilevatore) =>
        db.Ispezioni
            .Where(x => x.Rilevatore.ToLower() == rilevatore.ToLower())
            .Include(x => x.Schede)
            .ThenInclude(s => s.Articoli)
            .AsNoTracking()
            .OrderByDescending(x => x.Data)
            .ToListAsync();

    public async Task AddIspezioneAsync(Ispezione ispezione)
    {
        db.Ispezioni.Add(ispezione);
        await db.SaveChangesAsync();
    }

    public async Task<Ispezione> GetOrCreateIspezioneAsync(string codMdep, DateTime data, string rilevatore)
    {
        var existing = await db.Ispezioni
            .AsNoTracking()
            .FirstOrDefaultAsync(x =>
                x.CodMdep == codMdep &&
                x.Data == data &&
                x.Rilevatore == rilevatore);

        if (existing != null)
            return existing;

        var created = new Ispezione
        {
            CodMdep = codMdep,
            Data = data,
            Rilevatore = rilevatore
        };

        db.Ispezioni.Add(created);
        await db.SaveChangesAsync();

        return await db.Ispezioni
            .AsNoTracking()
            .FirstAsync(x =>
                x.CodMdep == codMdep &&
                x.Data == data &&
                x.Rilevatore == rilevatore);
    }

    public async Task<bool> UpdateIspezioneAsync(Ispezione ispezione)
    {
        db.Ispezioni.Update(ispezione);
        await db.SaveChangesAsync();
        return true;
    }

    public async Task<bool> UpdateStatoIspezioneAsync(string codMdep, DateTime data, string rilevatore,
        StatusEnum stato)
    {
        var ispezione = await db.Ispezioni
            .FirstOrDefaultAsync(x =>
                x.CodMdep == codMdep &&
                x.Data == data &&
                x.Rilevatore == rilevatore);

        if (ispezione is null)
            return false;

        ispezione.Stato = stato;
        db.Ispezioni.Update(ispezione);
        await db.SaveChangesAsync();
        return true;
    }

    public async Task<bool> UpdateActivityIdIspezioneAsync(string codMdep, DateTime data, string rilevatore,
        string? activityId)
    {
        var ispezione = await db.Ispezioni
            .FirstOrDefaultAsync(x =>
                x.CodMdep == codMdep &&
                x.Data == data &&
                x.Rilevatore == rilevatore);

        if (ispezione is null)
            return false;

        ispezione.ActivityId = activityId;
        db.Ispezioni.Update(ispezione);
        await db.SaveChangesAsync();
        return true;
    }

    /// <summary>
    /// Cancella l'ispezione e tutte le schede collegate (e relativi articoli via cascade).
    /// </summary>
    public async Task<bool> DeleteIspezioneAsync(string codMdep, DateTime data, string rilevatore)
    {
        var schede = await GetAllSchedeOfIspezioneAsync(codMdep, data, rilevatore);
        if (schede.Count > 0) 
            throw new Exception("Impossibile cancellare l'ispezione perché ci sono schede collegate.");

        var ispezione = await db.Ispezioni
            .FirstOrDefaultAsync(x =>
                x.CodMdep == codMdep &&
                x.Data == data &&
                x.Rilevatore == rilevatore);

        if (ispezione is null)
            return false;

        db.Ispezioni.Remove(ispezione);
        await db.SaveChangesAsync();
        return true;
    }

    public async Task AddSchedaAsync(string codMdep, DateTime data, string rilevatore, Scheda scheda)
    {
        await GetOrCreateIspezioneAsync(codMdep, data, rilevatore);

        scheda.CodMdep = codMdep;
        scheda.Data = data;
        scheda.Rilevatore = rilevatore;

        if (scheda.Articoli is { Count: > 0 })
        {
            foreach (var a in scheda.Articoli)
            {
                a.SchedaId = scheda.Id;
            }
        }

        db.Schede.Add(scheda);
        await db.SaveChangesAsync();
    }

    public Task<List<Scheda>> GetAllSchedeOfIspezioneAsync(string codMdep, DateTime data, string rilevatore) =>
        db.Schede
            .AsNoTracking()
            .Include(s => s.Articoli)
            .Where(x => x.CodMdep == codMdep &&
                        x.Data == data &&
                        x.Rilevatore == rilevatore)
            .ToListAsync();

    public Task<Scheda?> GetSchedaAsync(int schedaId) =>
        db.Schede
            .AsNoTracking()
            .Include(s => s.Articoli)
            .FirstOrDefaultAsync(x => x.Id == schedaId);

    public Task<Scheda?> GetSchedaWithIspezioneAsync(int schedaId) =>
        db.Schede
            .Include(x => x.Ispezione)
            .Include(x => x.Articoli)
            .AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == schedaId);

    public async Task<bool> UpdateActivityIdSchedaAsync(int schedaId, string? activityId)
    {
        var scheda = await db.Schede.FirstOrDefaultAsync(x => x.Id == schedaId);
        if (scheda is null) return false;

        scheda.ActivityId = activityId;
        db.Schede.Update(scheda);
        await db.SaveChangesAsync();
        return true;
    }

    public async Task<bool> UpdateFileListSchedaAsync(int schedaId, List<string>? imageNames)
    {
        var scheda = await db.Schede.FirstOrDefaultAsync(x => x.Id == schedaId);
        if (scheda is null) return false;

        scheda.ImageNames = imageNames;
        db.Schede.Update(scheda);
        await db.SaveChangesAsync();
        return true;
    }

    public async Task<bool> DeleteSchedaAsync(int schedaId)
    {
        var scheda = await db.Schede.FirstOrDefaultAsync(x => x.Id == schedaId);
        if (scheda is null)
            return false;

        db.Schede.Remove(scheda);
        await db.SaveChangesAsync();
        return true;
    }

    public async Task<bool> UpdateSchedaAsync(Scheda scheda)
    {
        var existing = await db.Schede
            .Include(s => s.Articoli)
            .FirstOrDefaultAsync(s => s.Id == scheda.Id);

        if (existing is null)
            return false;

        db.Entry(existing).CurrentValues.SetValues(scheda);

        var incoming = scheda.Articoli;

        foreach (var toRemove in existing.Articoli
                     .Where(ea => incoming.All(ia => ia.Id != ea.Id))
                     .ToList())
        {
            existing.Articoli.Remove(toRemove);
        }

        foreach (var ia in incoming)
        {
            if (ia.Id == 0)
            {
                existing.Articoli.Add(new SchedaArticolo
                {
                    Barcode = ia.Barcode,
                    Descrizione = ia.Descrizione,
                    SchedaId = existing.Id
                });
            }
            else
            {
                var ea = existing.Articoli.FirstOrDefault(x => x.Id == ia.Id);
                if (ea is null)
                {
                    existing.Articoli.Add(new SchedaArticolo
                    {
                        Id = ia.Id,
                        Barcode = ia.Barcode,
                        Descrizione = ia.Descrizione,
                        SchedaId = existing.Id
                    });
                }
                else
                {
                    ea.Barcode = ia.Barcode;
                    ea.Descrizione = ia.Descrizione;
                }
            }
        }

        await db.SaveChangesAsync();
        return true;
    }

    /// <summary>
    /// Cancella tutte le schede di una ispezione senza cancellare l'ispezione.
    /// </summary>
    public async Task<int> DeleteAllSchedeOfIspezioneAsync(string codMdep, DateTime data, string rilevatore)
    {
        var schede = await db.Schede
            .Where(s =>
                s.CodMdep == codMdep &&
                s.Data == data &&
                s.Rilevatore == rilevatore)
            .ToListAsync();

        if (schede.Count == 0)
            return 0;

        db.Schede.RemoveRange(schede);
        return await db.SaveChangesAsync();
    }
}