using Apollinare.Domain.Entities;
using Apollinare.Domain.Enums;
using Apollinare.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace Apollinare.API.Services;

public class DemoDataService
{
    private readonly AppollinareDbContext _context;
    private readonly Random _random = new();

    public DemoDataService(AppollinareDbContext context)
    {
        _context = context;
    }

    /// <summary>
    /// Genera dati di test per dimostrazioni
    /// </summary>
    public async Task<DemoDataResult> GenerateDemoData()
    {
        var result = new DemoDataResult();

        // 1. Genera Clienti
        var clienti = GenerateClienti();
        _context.Clienti.AddRange(clienti);
        result.ClientiCreati = clienti.Count;

        // 2. Genera Location
        var locations = GenerateLocations();
        _context.Location.AddRange(locations);
        result.LocationCreate = locations.Count;

        // 3. Genera Risorse
        var risorse = GenerateRisorse();
        _context.Risorse.AddRange(risorse);
        result.RisorseCreate = risorse.Count;

        // 4. Genera Articoli
        var articoli = GenerateArticoli();
        _context.Articoli.AddRange(articoli);
        result.ArticoliCreati = articoli.Count;

        await _context.SaveChangesAsync();

        // 5. Genera Eventi (dopo aver salvato le entità correlate)
        var eventi = await GenerateEventi(clienti, locations);
        _context.Eventi.AddRange(eventi);
        result.EventiCreati = eventi.Count;

        await _context.SaveChangesAsync();

        return result;
    }

    /// <summary>
    /// Pulisce tutti i dati di test dal database
    /// </summary>
    public async Task<DemoDataResult> ClearDemoData()
    {
        var result = new DemoDataResult();

        // Elimina in ordine per rispettare le foreign key

        // Dettagli eventi
        var altriCosti = await _context.Set<EventoAltroCosto>().ToListAsync();
        _context.Set<EventoAltroCosto>().RemoveRange(altriCosti);

        var acconti = await _context.Set<EventoAcconto>().ToListAsync();
        _context.Set<EventoAcconto>().RemoveRange(acconti);

        var detOspiti = await _context.Set<EventoDettaglioOspiti>().ToListAsync();
        _context.Set<EventoDettaglioOspiti>().RemoveRange(detOspiti);

        var detPrelievo = await _context.Set<EventoDettaglioPrelievo>().ToListAsync();
        _context.Set<EventoDettaglioPrelievo>().RemoveRange(detPrelievo);

        var detRisorse = await _context.Set<EventoDettaglioRisorsa>().ToListAsync();
        _context.Set<EventoDettaglioRisorsa>().RemoveRange(detRisorse);

        var degustazioni = await _context.Set<EventoDegustazione>().ToListAsync();
        _context.Set<EventoDegustazione>().RemoveRange(degustazioni);

        // Eventi
        var eventi = await _context.Eventi.ToListAsync();
        _context.Eventi.RemoveRange(eventi);
        result.EventiCreati = eventi.Count;

        // Articoli
        var articoli = await _context.Articoli.ToListAsync();
        _context.Articoli.RemoveRange(articoli);
        result.ArticoliCreati = articoli.Count;

        // Risorse
        var risorse = await _context.Risorse.ToListAsync();
        _context.Risorse.RemoveRange(risorse);
        result.RisorseCreate = risorse.Count;

        // Location
        var locations = await _context.Location.ToListAsync();
        _context.Location.RemoveRange(locations);
        result.LocationCreate = locations.Count;

        // Clienti
        var clienti = await _context.Clienti.ToListAsync();
        _context.Clienti.RemoveRange(clienti);
        result.ClientiCreati = clienti.Count;

        await _context.SaveChangesAsync();

        return result;
    }

    private List<Cliente> GenerateClienti()
    {
        var nomi = new[] { "Rossi", "Bianchi", "Verdi", "Ferrari", "Esposito", "Romano", "Colombo", "Ricci", "Marino", "Greco" };
        var prenomi = new[] { "Mario", "Giuseppe", "Giovanni", "Francesco", "Antonio", "Luigi", "Marco", "Paolo", "Andrea", "Luca" };
        var citta = new[] { "Milano", "Roma", "Napoli", "Torino", "Firenze", "Bologna", "Venezia", "Genova", "Palermo", "Bari" };

        return Enumerable.Range(1, 15).Select(i => new Cliente
        {
            RagioneSociale = $"{prenomi[_random.Next(prenomi.Length)]} {nomi[_random.Next(nomi.Length)]}",
            Indirizzo = $"Via Roma {_random.Next(1, 200)}",
            Cap = $"{_random.Next(10000, 99999)}",
            Citta = citta[_random.Next(citta.Length)],
            Provincia = "MI",
            Telefono = $"02{_random.Next(1000000, 9999999)}",
            Email = $"cliente{i}@example.com",
            CodiceFiscale = GenerateCodiceFiscale(),
            PartitaIva = $"{_random.NextInt64(10000000000, 99999999999)}",
            Attivo = true
        }).ToList();
    }

    private List<Location> GenerateLocations()
    {
        var nomiLocation = new[]
        {
            ("Villa dei Cedri", "Località Cedri 1", "Varese"),
            ("Castello Visconteo", "Via Castello 15", "Milano"),
            ("Tenuta del Sole", "Strada Provinciale 22", "Pavia"),
            ("Palazzo Reale", "Piazza Duomo 1", "Milano"),
            ("Villa Borromeo", "Via Borromeo 8", "Stresa"),
            ("Cascina La Vigne", "Via Campagna 45", "Bergamo"),
            ("Hotel Grand Palace", "Viale Europa 100", "Como"),
            ("Ristorante Bellavista", "Via Panoramica 12", "Lecco"),
            ("Agriturismo Il Casale", "Contrada Casale", "Cremona"),
            ("Country Club Elite", "Via Golf 5", "Monza")
        };

        return nomiLocation.Select((loc, i) => new Location
        {
            Nome = loc.Item1,
            Indirizzo = loc.Item2,
            Citta = loc.Item3,
            Provincia = "LO",
            Cap = $"{_random.Next(20000, 28000)}",
            Telefono = $"02{_random.Next(1000000, 9999999)}",
            Email = $"location{i + 1}@example.com",
            DistanzaKm = _random.Next(5, 80),
            Attivo = true
        }).ToList();
    }

    private List<Risorsa> GenerateRisorse()
    {
        var risorse = new List<(string Nome, string Cognome, string Tipo)>
        {
            ("Marco", "Belli", "Cameriere"),
            ("Laura", "Conti", "Cameriera"),
            ("Giuseppe", "Ferrara", "Cuoco"),
            ("Anna", "Galli", "Aiuto Cuoco"),
            ("Roberto", "Longo", "Cameriere"),
            ("Francesca", "Mancini", "Hostess"),
            ("Alessandro", "Neri", "Barman"),
            ("Chiara", "Orlando", "Cameriera"),
            ("Davide", "Parisi", "Responsabile Sala"),
            ("Elena", "Quattrocchi", "Pasticcera"),
            ("Fabio", "Rizzo", "Autista"),
            ("Giulia", "Santoro", "Cameriera"),
        };

        return risorse.Select(r => new Risorsa
        {
            Nome = r.Nome,
            Cognome = r.Cognome,
            Telefono = $"333{_random.Next(1000000, 9999999)}",
            Email = $"{r.Nome.ToLower()}.{r.Cognome.ToLower()}@example.com",
            Note = r.Tipo,
            Attivo = true
        }).ToList();
    }

    private List<Articolo> GenerateArticoli()
    {
        var articoli = new List<(string Codice, string Descrizione, string Um, int Qta)>
        {
            ("PIR001", "Piatto piano cm 27", "PZ", 200),
            ("PIR002", "Piatto fondo cm 23", "PZ", 150),
            ("PIR003", "Piatto dessert cm 19", "PZ", 180),
            ("BIC001", "Bicchiere acqua", "PZ", 250),
            ("BIC002", "Bicchiere vino rosso", "PZ", 200),
            ("BIC003", "Bicchiere vino bianco", "PZ", 200),
            ("BIC004", "Flute champagne", "PZ", 150),
            ("POS001", "Forchetta tavola", "PZ", 250),
            ("POS002", "Coltello tavola", "PZ", 250),
            ("POS003", "Cucchiaio tavola", "PZ", 200),
            ("POS004", "Forchetta dolce", "PZ", 180),
            ("TOV001", "Tovaglia bianca cm 180x180", "PZ", 50),
            ("TOV002", "Tovagliolo stoffa bianco", "PZ", 300),
            ("CEN001", "Centrotavola fiori", "PZ", 30),
            ("CAN001", "Candelabro 5 braccia", "PZ", 20),
            ("VAS001", "Vassoio ovale grande", "PZ", 40),
            ("VAS002", "Vassoio rettangolare", "PZ", 35),
            ("CAR001", "Carrello portavivande", "PZ", 8),
            ("SCA001", "Scaldavivande inox", "PZ", 15),
            ("SEG001", "Segnaposto elegante", "PZ", 200),
        };

        return articoli.Select(a => new Articolo
        {
            Codice = a.Codice,
            Descrizione = a.Descrizione,
            UnitaMisura = a.Um,
            QtaDisponibile = a.Qta,
            QtaStdA = Math.Round((decimal)_random.Next(1, 3) + (decimal)_random.NextDouble(), 2),
            QtaStdB = Math.Round((decimal)_random.Next(1, 2) + (decimal)_random.NextDouble(), 2),
            QtaStdS = Math.Round((decimal)_random.Next(1, 4) + (decimal)_random.NextDouble(), 2),
            Attivo = true
        }).ToList();
    }

    private async Task<List<Evento>> GenerateEventi(List<Cliente> clienti, List<Location> locations)
    {
        var tipiEvento = await _context.TipiEvento.ToListAsync();
        var tipiOspite = await _context.TipiOspite.ToListAsync();

        var descrizioniEvento = new[]
        {
            "Matrimonio {0}",
            "Battesimo {0}",
            "Comunione {0}",
            "Cresima {0}",
            "Compleanno {0}",
            "Anniversario {0}",
            "Festa aziendale {0}",
            "Gala di beneficenza {0}",
            "Laurea {0}",
            "Festa privata {0}"
        };

        var eventi = new List<Evento>();
        var baseDate = DateTime.Today;

        // Genera 20 eventi distribuiti nel tempo
        for (int i = 0; i < 20; i++)
        {
            var cliente = clienti[_random.Next(clienti.Count)];
            var location = locations[_random.Next(locations.Count)];
            var tipoEvento = tipiEvento.Count > 0 ? tipiEvento[_random.Next(tipiEvento.Count)] : null;

            // Date distribuite: alcuni passati, alcuni futuri
            var daysOffset = _random.Next(-30, 90);
            var dataEvento = baseDate.AddDays(daysOffset);

            var numeroOspiti = _random.Next(30, 200);
            var costoPersona = _random.Next(50, 150);
            var stato = daysOffset < -7 ? StatoEvento.Confermato :
                       (daysOffset < 0 ? StatoEvento.Confermato :
                       (_random.Next(3) == 0 ? StatoEvento.Scheda :
                       (_random.Next(2) == 0 ? StatoEvento.Preventivo : StatoEvento.Confermato)));

            var evento = new Evento
            {
                Codice = $"EVT{baseDate.Year}{(i + 1):D4}",
                DataEvento = dataEvento,
                OraInizio = new TimeSpan(_random.Next(11, 19), 0, 0),
                OraFine = new TimeSpan(_random.Next(20, 24), 0, 0),
                ClienteId = cliente.Id,
                LocationId = location.Id,
                TipoEventoId = tipoEvento?.Id,
                Descrizione = string.Format(descrizioniEvento[_random.Next(descrizioniEvento.Length)], cliente.RagioneSociale),
                NumeroOspiti = numeroOspiti,
                CostoPersona = costoPersona,
                CostoTotale = numeroOspiti * costoPersona,
                Stato = stato,
                Confermato = stato == StatoEvento.Confermato,
                NoteInterne = "Evento generato automaticamente per demo",
                DataScadenzaPreventivo = dataEvento.AddDays(-14)
            };

            // Aggiungi dettagli ospiti
            if (tipiOspite.Count > 0)
            {
                var adulti = (int)(numeroOspiti * 0.85);
                var bambini = numeroOspiti - adulti;

                evento.DettagliOspiti = new List<EventoDettaglioOspiti>
                {
                    new EventoDettaglioOspiti
                    {
                        TipoOspiteId = tipiOspite.FirstOrDefault(t => t.Codice == "A")?.Id ?? tipiOspite[0].Id,
                        Numero = adulti,
                        CostoUnitario = costoPersona
                    }
                };

                if (bambini > 0 && tipiOspite.Any(t => t.Codice == "B"))
                {
                    evento.DettagliOspiti.Add(new EventoDettaglioOspiti
                    {
                        TipoOspiteId = tipiOspite.First(t => t.Codice == "B").Id,
                        Numero = bambini,
                        CostoUnitario = costoPersona * 0.5m
                    });
                }
            }

            // Aggiungi altri costi per alcuni eventi
            if (_random.Next(2) == 0)
            {
                evento.AltriCosti = new List<EventoAltroCosto>
                {
                    new EventoAltroCosto
                    {
                        Descrizione = "Allestimento floreale",
                        CostoUnitario = _random.Next(200, 800),
                        Quantita = 1,
                        ApplicaIva = true,
                        AliquotaIva = 22,
                        Ordine = 10
                    },
                    new EventoAltroCosto
                    {
                        Descrizione = "Servizio fotografico",
                        CostoUnitario = _random.Next(300, 1000),
                        Quantita = 1,
                        ApplicaIva = true,
                        AliquotaIva = 22,
                        Ordine = 20
                    }
                };
            }

            // Aggiungi acconti per eventi confermati
            if (stato == StatoEvento.Confermato)
            {
                var totale = evento.CostoTotale ?? 0;
                evento.Acconti = new List<EventoAcconto>
                {
                    new EventoAcconto
                    {
                        Descrizione = "Prima caparra (30%)",
                        Importo = Math.Round(totale * 0.30m, 2),
                        DataPagamento = dataEvento.AddDays(-60),
                        AConferma = true,
                        Ordine = 10,
                        MetodoPagamento = "Bonifico"
                    },
                    new EventoAcconto
                    {
                        Descrizione = "Seconda caparra (50%)",
                        Importo = Math.Round(totale * 0.50m, 2),
                        DataPagamento = daysOffset < -30 ? dataEvento.AddDays(-30) : null,
                        AConferma = false,
                        Ordine = 20
                    },
                    new EventoAcconto
                    {
                        Descrizione = "Saldo finale (20%)",
                        Importo = Math.Round(totale * 0.20m, 2),
                        DataPagamento = daysOffset < 0 ? dataEvento.AddDays(7) : null,
                        AConferma = false,
                        Ordine = 30
                    }
                };

                evento.TotaleAcconti = evento.Acconti
                    .Where(a => a.DataPagamento.HasValue)
                    .Sum(a => a.Importo);
                evento.Saldo = totale - (evento.TotaleAcconti ?? 0);
            }

            eventi.Add(evento);
        }

        return eventi;
    }

    private string GenerateCodiceFiscale()
    {
        const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        const string nums = "0123456789";

        var cf = new char[16];
        for (int i = 0; i < 6; i++) cf[i] = chars[_random.Next(chars.Length)];
        for (int i = 6; i < 8; i++) cf[i] = nums[_random.Next(nums.Length)];
        cf[8] = chars[_random.Next(chars.Length)];
        for (int i = 9; i < 11; i++) cf[i] = nums[_random.Next(nums.Length)];
        cf[11] = chars[_random.Next(chars.Length)];
        for (int i = 12; i < 15; i++) cf[i] = nums[_random.Next(nums.Length)];
        cf[15] = chars[_random.Next(chars.Length)];

        return new string(cf);
    }
}

public class DemoDataResult
{
    public int ClientiCreati { get; set; }
    public int LocationCreate { get; set; }
    public int RisorseCreate { get; set; }
    public int ArticoliCreati { get; set; }
    public int EventiCreati { get; set; }
    public string Message => $"Creati: {ClientiCreati} clienti, {LocationCreate} location, {RisorseCreate} risorse, {ArticoliCreati} articoli, {EventiCreati} eventi";
}
