using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using RevitEstimate.Models;

namespace RevitEstimate.Calculators
{
    public class Item01Calculator : IItemCalculator
    {
        private ProjectLevels _levels;

        public Item01Calculator(ProjectLevels levels = null)
        {
            _levels = levels ?? new ProjectLevels();
        }

        public List<EstimateRowModel> Calculate(List<Element> collector, out double totalQty)
        {
            totalQty = 0.0;
            var rows = new List<EstimateRowModel>();

            // 1. Outer Walls Layout (First Row)
            var walls = collector.Where(e => e.Category != null && e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Walls).ToList();
            if (walls.Count > 0)
            {
                double buildingLength = 10.0;
                double buildingWidth = 8.0;

                try
                {
                    BoundingBoxXYZ totalBox = null;
                    foreach (var wall in walls)
                    {
                        BoundingBoxXYZ box = wall.get_BoundingBox(null);
                        if (box != null)
                        {
                            if (totalBox == null) totalBox = box;
                            else
                            {
                                totalBox.Min = new XYZ(Math.Min(totalBox.Min.X, box.Min.X), Math.Min(totalBox.Min.Y, box.Min.Y), Math.Min(totalBox.Min.Z, box.Min.Z));
                                totalBox.Max = new XYZ(Math.Max(totalBox.Max.X, box.Max.X), Math.Max(totalBox.Max.Y, box.Max.Y), Math.Max(totalBox.Max.Z, box.Max.Z));
                            }
                        }
                    }

                    if (totalBox != null)
                    {
                        buildingLength = UnitUtils.ConvertFromInternalUnits(Math.Abs(totalBox.Max.X - totalBox.Min.X), UnitTypeId.Meters);
                        buildingWidth = UnitUtils.ConvertFromInternalUnits(Math.Abs(totalBox.Max.Y - totalBox.Min.Y), UnitTypeId.Meters);
                    }
                }
                catch (Exception) { }

                double finalLength1 = buildingLength + 5.0;
                double finalWidth1 = buildingWidth + 5.0;

                double depthPL_GL = Math.Abs(_levels.TopOfPlinth - _levels.GroundLevel);
                if (depthPL_GL <= 0) depthPL_GL = 1.5;

                double qty1 = finalLength1 * finalWidth1 * depthPL_GL;
                totalQty += qty1;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = "   -> Foundation Excavation Footprint (Outer Building Layout with 5m Offset)",
                    NosOfFlats = "1",
                    NosOfInstance = "1",
                    Length = finalLength1.ToString("F2"),
                    Width = finalWidth1.ToString("F2"),
                    HeightDepth = depthPL_GL.ToString("F2"),
                    Qty = qty1.ToString("F2"),
                    Unit = "Cum",
                    Remarks = $"Outer length + 5m ({finalLength1:F2}m), width + 5m ({finalWidth1:F2}m), depth (PL-GL) ({depthPL_GL:F2}m)."
                });
            }

            // 2. Individual Foundations (Subsequent Rows)
            var foundations = collector.Where(e => e.Category != null && e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_StructuralFoundation).ToList();

            foreach (var elem in foundations)
            {
                string fName = elem.Name;
                GetElementDimensions(elem, out double lengthMeters, out double widthMeters, out double thicknessMeters);

                double finalLength2 = lengthMeters + 0.5;
                double finalWidth2 = widthMeters + 0.5;

                double depthVal = Math.Abs(_levels.TopOfFoundation - _levels.BottomOfFoundation);
                if (depthVal <= 0)
                {
                    depthVal = thicknessMeters > 0 ? thicknessMeters : 0.4;
                }

                double instanceQty = finalLength2 * finalWidth2 * depthVal;
                totalQty += instanceQty;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Excavation for Foundation: {fName} [ID: {elem.Id}]",
                    NosOfFlats = "1",
                    NosOfInstance = "1",
                    Length = finalLength2.ToString("F2"),
                    Width = finalWidth2.ToString("F2"),
                    HeightDepth = depthVal.ToString("F2"),
                    Qty = instanceQty.ToString("F2"),
                    Unit = "Cum",
                    Remarks = $"l+0.5m ({finalLength2:F2}m), b+0.5m ({finalWidth2:F2}m), depth from Top to Bottom Foundation ({depthVal:F2}m)."
                });
            }

            return rows;
        }

        private void GetElementDimensions(Element elem, out double lengthMeters, out double widthMeters, out double thicknessMeters)
        {
            lengthMeters = 0; widthMeters = 0; thicknessMeters = 0;
            Parameter lenParam = elem.LookupParameter("Length") ?? elem.LookupParameter("Foundation Length");
            Parameter widthParam = elem.LookupParameter("Width") ?? elem.LookupParameter("Foundation Width");
            Parameter thickParam = elem.LookupParameter("Thickness") ?? elem.LookupParameter("Foundation Thickness") ?? elem.LookupParameter("Depth");

            if (lenParam == null || widthParam == null || thickParam == null)
            {
                ElementType elemType = elem.Document.GetElement(elem.GetTypeId()) as ElementType;
                if (elemType != null)
                {
                    lenParam ??= elemType.LookupParameter("Length") ?? elemType.LookupParameter("Foundation Length");
                    widthParam ??= elemType.LookupParameter("Width") ?? elemType.LookupParameter("Foundation Width");
                    thickParam ??= elemType.LookupParameter("Thickness") ?? elemType.LookupParameter("Foundation Thickness") ?? elemType.LookupParameter("Depth");
                }
            }

            if (lenParam != null) lengthMeters = UnitUtils.ConvertFromInternalUnits(lenParam.AsDouble(), UnitTypeId.Meters);
            if (widthParam != null) widthMeters = UnitUtils.ConvertFromInternalUnits(widthParam.AsDouble(), UnitTypeId.Meters);
            if (thickParam != null) thicknessMeters = UnitUtils.ConvertFromInternalUnits(thickParam.AsDouble(), UnitTypeId.Meters);

            if (lengthMeters <= 0 || widthMeters <= 0)
            {
                BoundingBoxXYZ bbox = elem.get_BoundingBox(null);
                if (bbox != null)
                {
                    lengthMeters = UnitUtils.ConvertFromInternalUnits(Math.Abs(bbox.Max.X - bbox.Min.X), UnitTypeId.Meters);
                    widthMeters = UnitUtils.ConvertFromInternalUnits(Math.Abs(bbox.Max.Y - bbox.Min.Y), UnitTypeId.Meters);
                    if (thicknessMeters <= 0)
                        thicknessMeters = UnitUtils.ConvertFromInternalUnits(Math.Abs(bbox.Max.Z - bbox.Min.Z), UnitTypeId.Meters);
                }
                else
                {
                    lengthMeters = 1.5; widthMeters = 1.5; thicknessMeters = 0.4;
                }
            }
        }
    }
}