using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using foundation_system.Data;
using foundation_system.Models;
using foundation_system.Models.ViewModels;
using foundation_system.Services;
using Microsoft.AspNetCore.Authorization;

namespace foundation_system.Controllers;

[Authorize]
public class ExpedienteController : Controller
{
    private readonly ApplicationDbContext _context;
    private readonly IWebHostEnvironment _hostingEnvironment;
    private readonly IAntecedentesService _antecedentesService;

    public ExpedienteController(ApplicationDbContext context, IWebHostEnvironment hostingEnvironment, IAntecedentesService antecedentesService)
    {
        _context = context;
        _hostingEnvironment = hostingEnvironment;
        _antecedentesService = antecedentesService;
    }

    // GET: Expediente
    public async Task<IActionResult> Index(string search = "", int page = 1, int pageSize = 10)
    {
        var query = _context.Ninos
            .Include(n => n.Persona)
            .Where(n => n.Activo);

        List<long> searchIds = new();
        if (!string.IsNullOrWhiteSpace(search))
        {
            searchIds = await PostgresQueryExecutor.ExecuteQueryAsync(
                _context,
                "SELECT id FROM buscar_personas_v2(@term, 'NINO')",
                reader => reader.GetInt64(0),
                p => p.AddWithValue("term", search)
            );
            
            query = query.Where(n => searchIds.Contains(n.Id));
        }

        var totalItems = await query.CountAsync();
        var totalPages = (int)Math.Ceiling(totalItems / (double)pageSize);
        
        // Asegurar que la página esté en rango
        page = Math.Max(1, Math.Min(page, totalPages > 0 ? totalPages : 1));

        var ninos = await query
            .OrderByDescending(n => n.CreadoEn)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        if (!string.IsNullOrWhiteSpace(search))
        {
            // Preserve the order from the fuzzy search procedure
            ninos = ninos.OrderBy(n => searchIds.IndexOf(n.Id)).ToList();
        }

        ViewBag.Search = search;
        ViewBag.CurrentPage = page;
        ViewBag.TotalPages = totalPages;
        ViewBag.HasPreviousPage = page > 1;
        ViewBag.HasNextPage = page < totalPages;
        ViewBag.PageSize = pageSize;
            
        return View(ninos);
    }

    // GET: Expediente/Details/5
    public async Task<IActionResult> Details(long? id)
    {
        if (id == null) return NotFound();

        var nino = await _context.Ninos
            .Include(n => n.Persona)
            .FirstOrDefaultAsync(m => m.Id == id);

        if (nino == null) return NotFound();

        var model = await MapToExpedienteViewModel(nino);
        return View(model);
    }

    // GET: Expediente/Print/5
    public async Task<IActionResult> Print(long? id)
    {
        if (id == null) return NotFound();

        var nino = await _context.Ninos
            .Include(n => n.Persona)
            .FirstOrDefaultAsync(m => m.Id == id);

        if (nino == null) return NotFound();

        var model = await MapToExpedienteViewModel(nino);
        var antecedentes = await _antecedentesService.GetHistorialByNinoIdAsync(id.Value);
        
        ViewBag.Antecedentes = antecedentes;
        
        return View(model);
    }

    private async Task<ExpedienteViewModel> MapToExpedienteViewModel(Nino nino)
    {
        var model = new ExpedienteViewModel
        {
            Id = nino.Id,
            Nombres = nino.Persona.Nombres,
            Apellidos = nino.Persona.Apellidos,
            Dui = nino.Persona.Dui,
            Nit = nino.Persona.Nit,
            FechaNacimiento = nino.Persona.FechaNacimiento,
            Genero = nino.Persona.Genero,
            Email = nino.Persona.Email,
            Telefono = nino.Persona.Telefono,
            Direccion = nino.Persona.Direccion,
            FotoUrl = nino.Persona.FotoUrl,
            CodigoInscripcion = nino.CodigoInscripcion,
            FechaInscripcion = nino.FechaInscripcion,
            Estado = nino.Estado,
            NivelGrado = nino.NivelGrado,
            Alergias = nino.Alergias,
            ContactoEmergenciaNombre = nino.ContactoEmergenciaNombre,
            ContactoEmergenciaTelefono = nino.ContactoEmergenciaTelefono
        };

        var encargados = await _context.EncargadosNino
            .Include(e => e.Persona)
            .Where(e => e.NinoId == nino.Id)
            .ToListAsync();

        var padre = encargados.FirstOrDefault(e => e.Parentesco == "PADRE");
        if (padre != null)
        {
            model.PadreId = padre.PersonaId;
            model.PadreNombre = padre.Persona.NombreCompleto;
        }

        var madre = encargados.FirstOrDefault(e => e.Parentesco == "MADRE");
        if (madre != null)
        {
            model.MadreId = madre.PersonaId;
            model.MadreNombre = madre.Persona.NombreCompleto;
        }

        var encargado = encargados.FirstOrDefault(e => e.Parentesco == "ENCARGADO");
        if (encargado != null)
        {
            model.EncargadoId = encargado.PersonaId;
            model.EncargadoNombre = encargado.Persona.NombreCompleto;
        }

        return model;
    }

    // GET: Expediente/Create
    public IActionResult Create()
    {
        return View(new ExpedienteViewModel());
    }

    // POST: Expediente/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create(ExpedienteViewModel model)
    {
        if (ModelState.IsValid)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                var persona = new Persona
                {
                    Nombres = model.Nombres,
                    Apellidos = model.Apellidos,
                    Dui = model.Dui,
                    Nit = model.Nit,
                    FechaNacimiento = model.FechaNacimiento,
                    Genero = model.Genero,
                    Email = model.Email,
                    Telefono = null, // Los niños no tienen teléfono
                    Direccion = model.Direccion,
                    FotoUrl = string.IsNullOrEmpty(model.FotoUrl) ? "/Assets/default_avatar.png" : model.FotoUrl,
                    Activo = true
                };

                _context.Personas.Add(persona);
                await _context.SaveChangesAsync();

                // Generar Código de Inscripción automáticamente usando procedimiento almacenado
                var nuevoCodigo = await PostgresScalarExecutor.ExecuteAsync<string>(
                    _context,
                    "SELECT generar_codigo_inscripcion(@apellidos, @anio)",
                    p =>
                    {
                        p.AddWithValue("apellidos", persona.Apellidos);
                        p.AddWithValue("anio", model.FechaInscripcion.Year);
                    }
                );


                var nino = new Nino
                {
                    PersonaId = persona.Id,
                    CodigoInscripcion = nuevoCodigo,
                    FechaInscripcion = model.FechaInscripcion,
                    Estado = model.Estado,
                    NivelGrado = model.NivelGrado,
                    Alergias = model.Alergias,
                    ContactoEmergenciaNombre = model.ContactoEmergenciaNombre,
                    ContactoEmergenciaTelefono = model.ContactoEmergenciaTelefono,
                    Activo = true
                };

                _context.Ninos.Add(nino);
                await _context.SaveChangesAsync();

                // Procesar foto si se subió una
                if (model.FotoFile != null)
                {
                    var fotoUrl = await ProcessPhotoUpload(model.FotoFile, nino.CodigoInscripcion);
                    persona.FotoUrl = fotoUrl;
                    await _context.SaveChangesAsync();
                }

                // Guardar Padres si se seleccionaron
                if (model.PadreId.HasValue)
                {
                    _context.EncargadosNino.Add(new EncargadoNino
                    {
                        NinoId = nino.Id,
                        PersonaId = model.PadreId.Value,
                        Parentesco = "PADRE",
                        EsPrincipal = false
                    });
                }

                if (model.MadreId.HasValue)
                {
                    _context.EncargadosNino.Add(new EncargadoNino
                    {
                        NinoId = nino.Id,
                        PersonaId = model.MadreId.Value,
                        Parentesco = "MADRE",
                        EsPrincipal = false
                    });
                }

                if (model.EncargadoId.HasValue)
                {
                    _context.EncargadosNino.Add(new EncargadoNino
                    {
                        NinoId = nino.Id,
                        PersonaId = model.EncargadoId.Value,
                        Parentesco = "ENCARGADO",
                        EsPrincipal = true
                    });
                }

                if (model.PadreId.HasValue || model.MadreId.HasValue || model.EncargadoId.HasValue)
                {
                    await _context.SaveChangesAsync();
                }

                await transaction.CommitAsync();
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync();
                ModelState.AddModelError("", "Ocurrió un error al guardar el expediente.\n" + ex.Message);
            }
        }
        return View(model);
    }

    // GET: Expediente/Edit/5
    public async Task<IActionResult> Edit(long? id)
    {
        if (id == null) return NotFound();

        var nino = await _context.Ninos
            .Include(n => n.Persona)
            .FirstOrDefaultAsync(n => n.Id == id);

        if (nino == null) return NotFound();

        var model = new ExpedienteViewModel
        {
            Id = nino.Id,
            Nombres = nino.Persona.Nombres,
            Apellidos = nino.Persona.Apellidos,
            Dui = nino.Persona.Dui,
            Nit = nino.Persona.Nit,
            FechaNacimiento = nino.Persona.FechaNacimiento,
            Genero = nino.Persona.Genero,
            Email = nino.Persona.Email,
            Telefono = nino.Persona.Telefono,
            Direccion = nino.Persona.Direccion,
            FotoUrl = nino.Persona.FotoUrl,
            CodigoInscripcion = nino.CodigoInscripcion,
            FechaInscripcion = nino.FechaInscripcion,
            Estado = nino.Estado,
            NivelGrado = nino.NivelGrado,
            Alergias = nino.Alergias,
            ContactoEmergenciaNombre = nino.ContactoEmergenciaNombre,
            ContactoEmergenciaTelefono = nino.ContactoEmergenciaTelefono
        };

        // Cargar Padres/Encargados
        var encargados = await _context.EncargadosNino
            .Include(e => e.Persona)
            .Where(e => e.NinoId == id)
            .ToListAsync();

        var padre = encargados.FirstOrDefault(e => e.Parentesco == "PADRE");
        if (padre != null)
        {
            model.PadreId = padre.PersonaId;
            model.PadreNombre = $"{padre.Persona.Nombres} {padre.Persona.Apellidos}";
        }

        var madre = encargados.FirstOrDefault(e => e.Parentesco == "MADRE");
        if (madre != null)
        {
            model.MadreId = madre.PersonaId;
            model.MadreNombre = $"{madre.Persona.Nombres} {madre.Persona.Apellidos}";
        }

        var encargado = encargados.FirstOrDefault(e => e.Parentesco == "ENCARGADO");
        if (encargado != null)
        {
            model.EncargadoId = encargado.PersonaId;
            model.EncargadoNombre = $"{encargado.Persona.Nombres} {encargado.Persona.Apellidos}";
        }

        return View(model);
    }

    // POST: Expediente/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(long id, ExpedienteViewModel model)
    {
        if (id != model.Id) return NotFound();

        if (ModelState.IsValid)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                var nino = await _context.Ninos
                    .Include(n => n.Persona)
                    .FirstOrDefaultAsync(n => n.Id == id);

                if (nino == null) return NotFound();

                // Update Persona
                nino.Persona.Nombres = model.Nombres;
                nino.Persona.Apellidos = model.Apellidos;
                nino.Persona.Dui = model.Dui;
                nino.Persona.Nit = model.Nit;
                nino.Persona.FechaNacimiento = model.FechaNacimiento;
                nino.Persona.Genero = model.Genero;
                nino.Persona.Email = model.Email;
                nino.Persona.Telefono = null; // Los niños no tienen teléfono
                nino.Persona.Direccion = model.Direccion;
                
                // Procesar foto si se subió una nueva
                if (model.FotoFile != null)
                {
                    nino.Persona.FotoUrl = await ProcessPhotoUpload(model.FotoFile, nino.CodigoInscripcion);
                }
                else if (string.IsNullOrEmpty(nino.Persona.FotoUrl))
                {
                    nino.Persona.FotoUrl = "/Assets/default_avatar.png";
                }

                nino.Persona.ActualizadoEn = DateTime.UtcNow;

                // Update Nino
                nino.CodigoInscripcion = model.CodigoInscripcion;
                nino.FechaInscripcion = model.FechaInscripcion;
                nino.Estado = model.Estado;
                nino.NivelGrado = model.NivelGrado;
                nino.Alergias = model.Alergias;
                nino.ContactoEmergenciaNombre = model.ContactoEmergenciaNombre;
                nino.ContactoEmergenciaTelefono = model.ContactoEmergenciaTelefono;
                nino.ActualizadoEn = DateTime.UtcNow;

                await _context.SaveChangesAsync();

                // Actualizar Encargados (Eliminar y volver a agregar para simplicidad)
                var encargadosExistentes = await _context.EncargadosNino
                    .Where(e => e.NinoId == id)
                    .ToListAsync();
                _context.EncargadosNino.RemoveRange(encargadosExistentes);
                await _context.SaveChangesAsync();

                if (model.PadreId.HasValue)
                {
                    _context.EncargadosNino.Add(new EncargadoNino
                    {
                        NinoId = nino.Id,
                        PersonaId = model.PadreId.Value,
                        Parentesco = "PADRE",
                        EsPrincipal = false
                    });
                }

                if (model.MadreId.HasValue)
                {
                    _context.EncargadosNino.Add(new EncargadoNino
                    {
                        NinoId = nino.Id,
                        PersonaId = model.MadreId.Value,
                        Parentesco = "MADRE",
                        EsPrincipal = false
                    });
                }

                if (model.EncargadoId.HasValue)
                {
                    _context.EncargadosNino.Add(new EncargadoNino
                    {
                        NinoId = nino.Id,
                        PersonaId = model.EncargadoId.Value,
                        Parentesco = "ENCARGADO",
                        EsPrincipal = true
                    });
                }

                await _context.SaveChangesAsync();
                await transaction.CommitAsync();

                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync();
                ModelState.AddModelError("", "Ocurrió un error al actualizar el expediente.\n" + ex.Message);
            }
        }
        return View(model);
    }

    // POST: Expediente/Desactivar/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Desactivar(long id)
    {
        var nino = await _context.Ninos.FindAsync(id);
        if (nino != null)
        {
            nino.Activo = false;
            nino.Estado = "INACTIVO";
            nino.ActualizadoEn = DateTime.UtcNow;
            await _context.SaveChangesAsync();
        }
        return RedirectToAction(nameof(Index));
    }

    private bool NinoExists(long id)
    {
        return _context.Ninos.Any(e => e.Id == id);
    }

    // AJAX Actions for Parents
    [HttpGet]
    public async Task<IActionResult> SearchPersonas(string term, string type = "ENCARGADO")
    {
        if (string.IsNullOrWhiteSpace(term)) return Json(new List<object>());

        var personas = await PostgresQueryExecutor.ExecuteQueryAsync(
            _context,
            "SELECT id, text, score FROM buscar_personas_v2(@term, @type)",
            reader => new
            {
                id = reader.GetInt64(0),
                text = reader.GetString(1),
                score = reader.GetDouble(2)
            },
            paramsColl => {
                paramsColl.AddWithValue("term", term);
                paramsColl.AddWithValue("type", type);
            }
        );

        return Json(personas);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> CreatePersona([FromBody] Persona model)
    {
        if (ModelState.IsValid)
        {
            model.Activo = true;
            model.CreadoEn = DateTime.UtcNow;
            model.ActualizadoEn = DateTime.UtcNow;
            
            _context.Personas.Add(model);
            await _context.SaveChangesAsync();
            
            return Json(new { success = true, id = model.Id, text = $"{model.Nombres} {model.Apellidos} ({model.Dui ?? "Sin DUI"})" });
        }
        
        return Json(new { success = false, message = "Datos inválidos" });
    }

    private async Task<string> ProcessPhotoUpload(IFormFile file, string codigoExpediente)
    {
        if (file == null || file.Length == 0) return "/Assets/default_avatar.png";

        var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" };
        var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();

        if (!allowedExtensions.Contains(fileExtension)) return "/Assets/default_avatar.png";

        var uploadsFolder = Path.Combine(_hostingEnvironment.WebRootPath, "uploads", "fotos");
        if (!Directory.Exists(uploadsFolder))
        {
            Directory.CreateDirectory(uploadsFolder);
        }

        // Usar el código del expediente como nombre de archivo
        var fileName = codigoExpediente + fileExtension;
        var filePath = Path.Combine(uploadsFolder, fileName);

        using (var stream = new FileStream(filePath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }

        return $"/uploads/fotos/{fileName}";
    }

    public string ExisteFoto(string url)
    {
        if (string.IsNullOrEmpty(url))
            return "/Assets/default_avatar.png";

        var uploadsFolder = Path.Combine(_hostingEnvironment.WebRootPath, "uploads", "fotos");
        string[] parts = url.Split('/');
        string name = parts[^1];

        string fullpath = Path.Combine(uploadsFolder, name);

        if (System.IO.File.Exists(fullpath))
            return url;

        return "/Assets/default_avatar.png";
    }
}
