using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using bdHerramientaCACOA.db;
using bdHerramientaCACOA.dbcontext;
using bdHerramientaCACOA.extensiones;
using Google.Protobuf.WellKnownTypes;
using static bdHerramientaCACOA.CASA;

namespace bdHerramientaCACOA
{
    public class LICITACIONES
    {
        public class VariablesLICITACIONES
        {
            public double CostesVariables { get; set; } = 0.035;
            public int JornadaLaboral { get; set; } = 1800;
            public int HorasProduccionMedia { get; set; } = 80;
            public double IPCCosteIndirecto { get; set; } = 1;
        }
        public VariablesLICITACIONES Variables { get; set; } = new VariablesLICITACIONES();

        public string Descripcion { get; set; } = "Licitación Edificación " + DateTime.Now;
        public class EnumeradosLICITACIONES
        {
            public List<puntosinformacion> PuntosInformacion { get; set; } = new List<puntosinformacion>();

            public List<enumeracionesCASA> GruposTipologias { get; set; } = new List<enumeracionesCASA>();
            public List<enumeracionesCASA> Intervenciones { get; set; } = new List<enumeracionesCASA>();
            public List<enumeracionesCASA> ListaOtrosTrabajos { get; set; } = new List<enumeracionesCASA>();

            public List<tipologiasCASA> Tipologias { get; set; } = new List<tipologiasCASA>();
            //public List<costedespachoprofesional> ListaCostesDespacho { get; set; } = new List<costedespachoprofesional>();

            public List<ConvenioColectivoConcurso> ListaConvenioColectivo { get; set; } = new List<ConvenioColectivoConcurso>();
            public List<CosteDirectoIndirecto> ListaCDCI { get; set; } = new List<CosteDirectoIndirecto>();

            public List<tipologiasCASA> TipologiasPorGrupo(int idgrupo)
            {
                return Tipologias.Where(x => x.idGrupoTip == idgrupo).ToList();
            }
        }
        public EnumeradosLICITACIONES Enumerados { get; set; } = new EnumeradosLICITACIONES();
        public List<UsosTipologia> Usos { get; set; } = new List<UsosTipologia>();
        public List<FasesTrabajo> FasesTrabajo { get; set; } = new List<FasesTrabajo>();

        //Para el calculo de tabla
        public string idProvincia { get; set; } = "";
        public int idGradSup { get; set; } = 0;
        public int idGradMed { get; set; } = 0;
        public int idDelin { get; set; } = 0;

        public double IncrementoUrgencia { get; set; } = 0;
        public double PlazoPresentacionDocumentos { get; set; } = 0;
        public bool EsPorUsos { get; set; } = true;
        public double CostesProduccionTrabProf { get; set; } = 0;

        public double Beneficio { get; set; } = 19;

        public double PrecioTrabProf { get; set; } = 0;

        public List<OtrosTrabajos> OtrosTrabajos { get; set; } = new List<OtrosTrabajos>();
        public double TotalOtrosTrabajos { get; set; } = 0;
        public double PrecioDelEncargo { get; set; } = 0;

    

        public void Inicializar(bdHerramientaCACOA.dbcontext.tsHerramientasCACOA bd)
        {
            Variables.CostesVariables = (double)bd.enumeraciones.First(x => x.Codigo == "VARCASA.COSTESVARIABLES").ValorNumerico1;
            Variables.JornadaLaboral = (int)bd.enumeraciones.First(x => x.Codigo == "VARCASA.JORLABANUAL").ValorNumerico1;
            Variables.HorasProduccionMedia = (int)bd.enumeraciones.First(x => x.Codigo == "VARCASA.HORPRODMEDIA").ValorNumerico1;
            Variables.IPCCosteIndirecto = (double) bd.enumeraciones.First(x => x.Codigo == "VARCASA.IPCCD").ValorNumerico1;

            Beneficio = (double)bd.enumeraciones.First(x => x.Codigo == "VARCASA.GG+BI").ValorNumerico1;


            idGradSup = bd.enumeraciones.First(x => x.Codigo == "PUESTOTRABAJO.TECGRADSUP").idEnumeracion;
            idGradMed = bd.enumeraciones.First(x => x.Codigo == "PUESTOTRABAJO.GESTPROY").idEnumeracion;
            idDelin = bd.enumeraciones.First(x => x.Codigo == "PUESTOTRABAJO.DELINPROY").idEnumeracion;

            //Cargamos las Fases
            var grupoFases = bd.gruposenumeraciones.FirstOrDefault(x => x.Grupo == "FASES");
            var listaFases = Utilidades.devolverListadoOrdenadoEnumeracion(bd, grupoFases.idGrupoEnumeracion).ToList();

            foreach (enumeraciones enu in listaFases)
            {
                if (!FasesTrabajo.Any(x => x.Denominacion == enu.ValorAlfabetico1))
                {
                    FasesTrabajo ft = new FasesTrabajo();
                    ft.idFase = enu.idEnumeracion;
                    ft.Codigo = enu.Codigo;
                    ft.Denominacion = enu.ValorAlfabetico1;
                    ft.Porcentaje = (double)enu.ValorNumerico1;
                    ft.PlazoMinimo = enu.ValorNumerico2 == null ? null : (double)enu.ValorNumerico2;
                    FasesTrabajo.Add(ft);
                }
            }

            checkListCostIndirectos(listaFases);


            Enumerados.PuntosInformacion = bd.puntosinformacion.Where(x => x.idTipoHerramienta == 32).ToList();


            //Cargamos la lista de otros costes de trabajo
            var grupoOtrosTrab = bd.gruposenumeraciones.FirstOrDefault(x => x.Grupo == "OTROSTRAB");
            Enumerados.ListaOtrosTrabajos = ListaEnumeracionCASA(Utilidades.devolverListadoOrdenadoEnumeracion(bd, grupoOtrosTrab.idGrupoEnumeracion).ToList());

            //foreach (enumeraciones enu in listaOtrosTrab)
            //{
            //    OtrosTrabajos ot = new OtrosTrabajos();
            //    ot.idTrabajo = enu.idEnumeracion;
            //    ot.Denominacion = enu.ValorAlfabetico1;

            //    OtrosTrabajos.Add(ot);
            //}


            var grupoTipologias = bd.gruposenumeraciones.FirstOrDefault(x => x.Grupo == "GRUPTIP");
            Enumerados.GruposTipologias = ListaEnumeracionCASA(Utilidades.devolverListadoOrdenadoEnumeracion(bd, grupoTipologias.idGrupoEnumeracion).ToList());
            Enumerados.Tipologias = ListaTipologiasCASA(bd.tipologias.ToList());

            var grupoIntervencion = bd.gruposenumeraciones.FirstOrDefault(x => x.Grupo == "INTERVENCION");
            Enumerados.Intervenciones= ListaEnumeracionCASA(Utilidades.devolverListadoOrdenadoEnumeracion(bd, grupoIntervencion.idGrupoEnumeracion).ToList());

            //Esto es para la tabla de CDCI matematica, en configuracion puede verse como "calculo CD+CI"
            var lisConvCol = bd.conveniocolectivo.ToList();
            Enumerados.ListaConvenioColectivo = ListaConvenioColectivoCASA(lisConvCol);
            Enumerados.ListaCDCI = CalcularTabla("");

        }


        private void checkListCostIndirectos(List<enumeraciones> listaFases)
        {
            var itemsAEliminar = FasesTrabajo
                .Where(item => !listaFases.Any(x => x.ValorAlfabetico1 == item.Denominacion))
                .ToList();

            foreach (var item in itemsAEliminar)
            {
                FasesTrabajo.Remove(item);
            }
        }

        //Funciones Trabajo Profesional
        public void InsertarUso(UsosTipologia uso)
        {
            RellenarDatosUsoExtra(uso);
            Usos.Add(uso);
            CalcularHorasSuperficie();
        }
        public void ActualizarUso(UsosTipologia uso)
        {
            RellenarDatosUsoExtra(uso);
            int indice = Usos.ToList().IndexOf(uso);
            Usos[indice] = uso;
            CalcularHorasSuperficie();
        }
        public void EliminarUso(UsosTipologia uso)
        {
            Usos.Remove(uso);
            CalcularHorasSuperficie();
        }
        public void ActualizarFase(FasesTrabajo ft)
        {
            int indice = FasesTrabajo.ToList().IndexOf(ft);
            FasesTrabajo[indice] = ft;
            CalcularHorasSuperficie();

            //Comprobar si esta va aqui, porque sino no modifca el precio licitación/concurso
            CalcularCostesProduccion();
        }
        public void RellenarDatosUsoExtra(UsosTipologia uso)
        {
            tipologiasCASA tipologia = Enumerados.Tipologias.First(x => x.idTipologia == uso.idTipologia);
            enumeracionesCASA intervencion = Enumerados.Intervenciones.First(x => x.idEnumeracion == uso.idTipoIntervencion);
            uso.CoefTipologia = tipologia.CoeficienteUso;
            uso.CoefIntervencion = (double)intervencion.ValorNumerico1;
        }

        public double superficie { get; set; }=0;
        public double coefSupTotal { get; set; } = 0;
        public double coefsinredondeo { get; set; } = 0;
        public double coefTipoTotal { get; set; }=0;
        public double totalCoefiTipologia { get; set; } = 0;
        public double totalCoefIntervencion { get; set; } = 0;
        public double coefIntervencionTotal { get; set; } = 0;
        public double fasesEncargadas { get; set; } = 0;
        public double horasProduccion { get; set; } = 0;
        public double horasProduccionEstimacion { get; set; } = 0;
        public double horasPorcentaje { get; set; } = 0;
        public double horasDocumentacion { get; set; } = 0;
        public double plazoMedio { get; set; } = 0;
        public double coefPlazos { get; set; } = 0;
        public double horasAnualesMinimas { get; set; } = 0;
        public double CD { get; set; } = 0;
        public double CI { get; set; } = 0;
        public double CosteCDCI { get; set; } = 0;
        public double tasaCostesVariables { get; set; } = 0;
        public double costeHoraProduccion { get; set; } = 0;
        public void CalcularHorasSuperficie() {
            // calcular superficie total
            superficie = Usos.Sum(x => x.superficie);
            if (superficie > 0 || !EsPorUsos) {
                // calcular coef superficie total
                coefsinredondeo = -0.1375 * Math.Log(superficie) + 2.4;
                 coefSupTotal = Math.Round( coefsinredondeo,2, MidpointRounding.AwayFromZero);

                totalCoefiTipologia = 0;
                totalCoefIntervencion = 0;
                // calcular coef tipologia total
                foreach (UsosTipologia uso in Usos) {
                    totalCoefiTipologia += uso.superficie * uso.CoefTipologia;
                    totalCoefIntervencion += uso.superficie * uso.CoefIntervencion;
                } 

             //totalCoefiTipologia = Usos.Sum(x => x.superficie) * Usos.Sum(x => x.CoefTipologia);
             coefTipoTotal = Math.Round(totalCoefiTipologia / superficie, 2, MidpointRounding.AwayFromZero);

            // calcular coef intervencion total
            // totalCoefIntervencion = Usos.Sum(x => x.superficie) * Usos.Sum(x => x.CoefIntervencion);
             coefIntervencionTotal = Math.Round(totalCoefIntervencion / superficie,2, MidpointRounding.AwayFromZero);

            // calcular fases encargadas
             fasesEncargadas = FasesTrabajo.Where(x=>x.Seleccionado).Sum(x=> x.Porcentaje);

                // calcular horas produccion trabajo profesional
                if (EsPorUsos)
                {
                    horasProduccion = Math.Round((superficie * coefSupTotal * coefTipoTotal * coefIntervencionTotal * (fasesEncargadas / 100)), 2, MidpointRounding.AwayFromZero);
                    // calcular horas elaboracion documentacion
                    //horasPorcentaje = FasesTrabajo.Where(x => (x.Codigo.Contains("ESTPREVIO") || x.Codigo.Contains("ANTEPROYECTO") || x.Codigo.Contains("PROYBASICO") || x.Codigo.Contains("PROYEJEC"))).Sum(x => x.Porcentaje);
                    
                    // COMPROBAR SI ESTO ESTÁ BIEN, SINO EL COSTE HORA DE PRODUCCIÓN NO CAMBIA
                    horasPorcentaje = FasesTrabajo.Where(x=>x.Seleccionado).Sum(x => x.Porcentaje);
                    horasDocumentacion = Math.Round(superficie * coefSupTotal * coefTipoTotal * coefIntervencionTotal * (horasPorcentaje / 100), 2, MidpointRounding.AwayFromZero);

                    horasProduccionEstimacion = horasProduccion;
                }
                else {

                    // COMPROBAR SI ESTO ESTÁ BIEN, SINO EL COSTE HORA DE PRODUCCIÓN NO CAMBIA Y TAMBIÉN VER SI DEBE CAMBIAR HORAS TRABAJO PROFESIONAL
                    horasDocumentacion = Math.Round(horasProduccion * (fasesEncargadas / 100), 2, MidpointRounding.AwayFromZero); ;
                }


                // calcular plazo medio(meses) esto hay que preguntarlo
                plazoMedio = 3;
                var plazoElegido = plazoMedio;
                if (PlazoPresentacionDocumentos > plazoMedio) {
                    plazoElegido = PlazoPresentacionDocumentos;
                }

                // calcular coef plazos menores de 6 meses
                coefPlazos =0;
             double restaPlazos = plazoElegido - PlazoPresentacionDocumentos;
            if (restaPlazos > 0)
            {
                coefPlazos= 1+ restaPlazos*2* 0.01666;
            }else if (restaPlazos == 0)
            {
                coefPlazos = 1;
            }
            else
            {
                coefPlazos = 1;
            }
            coefPlazos = coefPlazos.DosDecimales();

            // calcular horasAnualesMinimas
             horasAnualesMinimas =   ((horasDocumentacion * 12) / plazoElegido).DosDecimales();

            // calcular CD
            //var numTrab3 = Enumerados.ListaCDCI.First(x => x.NumeroTrabajadores == 3);
             CD = 0;
            //if (horasAnualesMinimas < numTrab3.HorasProduccionAnuales)
            //{
            //    CD = numTrab3.CostesDirectos;
            //}
            //else {
                CD = ObtenerCDTabla(horasAnualesMinimas)*coefPlazos;
            //}
            CD = CD.DosDecimales();
            // calcular CI
             CI = 0;
            //if (horasAnualesMinimas < numTrab3.HorasProduccionAnuales)
            //{
            //    CI = numTrab3.CostesIndirectos;
            //}
            //else
            //{
                CI = ObtenerCITabla(horasAnualesMinimas) * coefPlazos;
            //}
            CI = CI.DosDecimales();

            // calcular Coste hora CD+CI
             CosteCDCI = CD + CI;

            // calcular Tasa costes variables
             tasaCostesVariables = Variables.CostesVariables;

            // calcular coste hora de produccion
             costeHoraProduccion = CosteCDCI * (1 + tasaCostesVariables);

                if (PlazoPresentacionDocumentos == 0)
                {
                    CostesProduccionTrabProf = 0;
                }
                else
                {
                    IncrementoUrgencia = CalcularIncremento();
                    if (IncrementoUrgencia == 0)
                    {
                        CostesProduccionTrabProf = (horasProduccion * coefPlazos * costeHoraProduccion).DosDecimales();
                    }
                    else
                    {
                        var incr = 1 + (IncrementoUrgencia / 100);
                        CostesProduccionTrabProf = ((horasProduccion * incr) * 
                            (coefPlazos * incr) *
                            (costeHoraProduccion * incr)).DosDecimales();
                    }
                }
                

                PrecioTrabProf = (CostesProduccionTrabProf * (1 + (Beneficio / 100))).DosDecimales();

                    CostesProduccionTrabProf = Math.Round(CostesProduccionTrabProf, 2, MidpointRounding.AwayFromZero);
                    PrecioTrabProf = Math.Round(PrecioTrabProf, 2, MidpointRounding.AwayFromZero);

                    PrecioDelEncargo = PrecioTrabProf + TotalOtrosTrabajos;
                

            }
        }

        //// OTROS TRABAJOS GESTION

        public void InsertarOtrosTrabajos(OtrosTrabajos ot)
        {
            OtrosTrabajos.Add(ot);
            CalcularCostesProduccion();
        }
        public void EliminarOtrosTrabajos(OtrosTrabajos ot)
        {
            OtrosTrabajos.Remove(ot);
            CalcularCostesProduccion();
        }
        public void ActualizarOtrosTrabajos(OtrosTrabajos ot)
        {
            int indice = OtrosTrabajos.ToList().IndexOf(ot);
            OtrosTrabajos[indice] = ot;

            CalcularCostesProduccion();
        }
        public void CalcularCostesProduccion()
        {
            TotalOtrosTrabajos = CalcularTotalOtrosTrabajos();
            PrecioDelEncargo = PrecioTrabProf + TotalOtrosTrabajos;
        }
        public double CalcularTotalOtrosTrabajos()
        {
            return OtrosTrabajos.Sum(x => x.Coste);
        }

        public List<enumeracionesCASA> ListaEnumeracionCASA(List<enumeraciones> lista)
        {

            List<enumeracionesCASA> lu = new List<enumeracionesCASA>();
            foreach (enumeraciones enu in lista)
            {
                lu.Add(EnumeracionACASA(enu));
            }
            return lu;
        }
        public enumeracionesCASA EnumeracionACASA(enumeraciones enu)
        {
            return new enumeracionesCASA()
            {
                idEnumeracion = enu.idEnumeracion,
                idGrupoEnumeracion = enu.idGrupoEnumeracion,
                Codigo = enu.Codigo,
                Descripcion = enu.Descripcion,
                ValorAlfabetico1 = enu.ValorAlfabetico1,
                ValorAlfabetico2 = enu.ValorAlfabetico2,
                ValorNumerico1 = enu.ValorNumerico1
            };
        }
        private double ObtenerCDTabla(double horas)
        {
            double valor = 0;
            var a = Enumerados.ListaCDCI.OrderByDescending(x => x.NumeroTrabajadores);
            valor= a.First().CostesDirectos;
            if (a.Count() > 0)
            {
                foreach (CosteDirectoIndirecto cdp in a)
                {
                    if (horas <= cdp.HorasProduccionAnuales)
                    {
                        valor = cdp.CostesDirectos;
                    }
                }
            }
            return valor;
        }
        private double ObtenerCITabla(double horas)
        {
            double valor = 0;
            var a = Enumerados.ListaCDCI.OrderByDescending(x => x.NumeroTrabajadores);
            if (a.Count() > 0) {
                valor = a.First().CostesIndirectos;
                foreach (CosteDirectoIndirecto cdp in a)
                {
                    if (horas <= cdp.HorasProduccionAnuales)
                    {
                        valor = cdp.CostesIndirectos;
                    }
                }
            }
            return valor;
        }
        public List<tipologiasCASA> ListaTipologiasCASA(List<tipologias> lista)
        {

            List<tipologiasCASA> lu = new List<tipologiasCASA>();
            foreach (tipologias enu in lista)
            {
                lu.Add(tipologiaACASA(enu));
            }
            return lu;
        }
        public tipologiasCASA tipologiaACASA(tipologias enu)
        {
            return new tipologiasCASA()
            {
                idTipologia = enu.idtipologia,
                idGrupoTip = enu.idGrupoTip,
                Descripcion = enu.Descripcion,
                CoeficienteUso = enu.CoeficienteUso
            };
        }
        public List<ConvenioColectivoConcurso> ListaConvenioColectivoCASA(List<conveniocolectivo> lista)
        {
            List<ConvenioColectivoConcurso> lcc = new List<ConvenioColectivoConcurso>();
            foreach (conveniocolectivo enu in lista)
            {
                lcc.Add(convenioColectivoACASA(enu));
            }
            return lcc;
        }
        public ConvenioColectivoConcurso convenioColectivoACASA(conveniocolectivo cc)
        {
            return new ConvenioColectivoConcurso()
            {
                idConvenioColectivo = cc.idConvenioColectivo,
                idEnumeracion = cc.idEnumeracion,
                idProvincia = cc.idProvincia,
                SalarioBase = cc.SalarioBase,
                OtrosCostes = cc.OtrosCostes,
                CosteAnualTrabajador = cc.CosteAnualTrabajador
            };
        }

        public List<CosteDirectoIndirecto> CalcularTabla(string idProvincia)
        {
            List<CosteDirectoIndirecto> listaCDCI = new List<CosteDirectoIndirecto>();
            //Obtenemos variables a utilizar
            if (idProvincia == "") { idProvincia = null; }

            double ValorTecSuperior =  Enumerados.ListaConvenioColectivo.FirstOrDefault(x => x.idProvincia == idProvincia && x.idEnumeracion == idGradSup) != null ? Enumerados.ListaConvenioColectivo.First(x => x.idProvincia == idProvincia && x.idEnumeracion == idGradSup).CosteAnualTrabajador : 0;
            double ValorTecMedio = Enumerados.ListaConvenioColectivo.FirstOrDefault(x => x.idProvincia == idProvincia && x.idEnumeracion == idGradMed) != null ? Enumerados.ListaConvenioColectivo.First(x => x.idProvincia == idProvincia && x.idEnumeracion == idGradMed).CosteAnualTrabajador : 0;
            double ValorDelineante = Enumerados.ListaConvenioColectivo.FirstOrDefault(x => x.idProvincia == idProvincia && x.idEnumeracion == idDelin) != null ? Enumerados.ListaConvenioColectivo.First(x => x.idProvincia == idProvincia && x.idEnumeracion == idDelin).CosteAnualTrabajador : 0; ;

            double masCaro = Math.Max(ValorTecSuperior, Math.Max(ValorTecMedio, ValorDelineante));
            double masBarato = Math.Min(ValorTecSuperior, Math.Min(ValorTecMedio, ValorDelineante));

            //Se calcula 
            //listaCDCI.Add(new CosteDirectoIndirecto(1, 1 * ValorTecSuperior, 1 * ValorTecSuperior, jornadaLaboralAnual, horasProdMedia, porcentajeCostesVariables));
            for (int i = 2; i <= 20; i++)
            {
                double costeMinimo = ValorTecSuperior + (masBarato * (i - 1));
                double costeMaximo = ValorTecSuperior + (masCaro * (i - 1));
                CosteDirectoIndirecto cdci = new CosteDirectoIndirecto(i, costeMinimo, costeMaximo, Variables.JornadaLaboral, Variables.HorasProduccionMedia, Variables.CostesVariables,Variables.IPCCosteIndirecto);
                listaCDCI.Add(cdci);
            }
            return listaCDCI;
        }

        public double CalcularIncremento()
        {
            double incremento = 3.5;

            var listaPlazos = FasesTrabajo.Where(x => x.Porcentaje > 0 && x.PlazoMinimo != null && x.Seleccionado==true).ToList();
            if (listaPlazos.Count > 0)
            {
                double meses = (double)listaPlazos.Sum(x => x.PlazoMinimo);
                if (PlazoPresentacionDocumentos < meses)
                {
                    return (meses - PlazoPresentacionDocumentos) * incremento;
                }
                else
                {
                    return 0;
                }
            }
            else
            {
                return 0;
            }

        }
        public class ConvenioColectivoConcurso
        {
            public int idConvenioColectivo { get; set; } = 0;
            public int idEnumeracion { get; set; } = 0;
            public string? idProvincia { get; set; } = "";
            public double? SalarioBase { get; set; } = 0;
            public double? OtrosCostes { get; set; } = 0;
            public double CosteAnualTrabajador { get; set; } = 0;
        }
    }
}
