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

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

        public Item15Calculator(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>();

            Document doc = collector.FirstOrDefault()?.Document;
            if (doc == null) return rows;

            // Collect all structural framing (beams) at plinth level
            var structuralFraming = collector.Where(e => e.Category != null && e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_StructuralFraming).ToList();

            foreach (var beam in structuralFraming)
            {
                double beamZ = GetElementZCoordinate(beam);
                if (beamZ > _levels.TopOfPlinth + 0.2 || beamZ < _levels.TopOfPlinth - 0.3) continue;

                string beamName = beam.Name;
                double cutLength = GetBeamCutLength(beam);

                // Fetch 'b' (width) from beam properties
                double bWid = GetParameterValueByName(beam, "b") ?? GetParameterValueByName(beam, "Width") ?? 0.3;
                if (cutLength <= 0) cutLength = 3.0;

                // Top surface area = Cut Length x b (No height/depth)
                double qty = cutLength * bWid;
                totalQty += qty;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Plinth Beam Top Surface (Item 15): {beamName} [ID: {beam.Id}]",
                    NosOfFlats = "1",
                    NosOfInstance = "1",
                    Length = cutLength.ToString("F2"),
                    Width = bWid.ToString("F2"),
                    HeightDepth = "", // No height/depth
                    Qty = qty.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Plinth beam top surface area (Cut Length={cutLength:F2}m x b={bWid:F2}m) for Item 15."
                });
            }

            if (rows.Count == 0)
            {
                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = "   -> [No plinth level beams found for Item 15]",
                    NosOfFlats = "-",
                    NosOfInstance = "0",
                    Length = "-",
                    Width = "-",
                    HeightDepth = "-",
                    Qty = "0.00",
                    Unit = "-",
                    Remarks = "No elements detected."
                });
            }

            return rows;
        }

        private double GetBeamCutLength(Element beam)
        {
            Parameter cutLenParam = beam.LookupParameter("Cut Length") ?? beam.get_Parameter(BuiltInParameter.INSTANCE_LENGTH_PARAM);
            if (cutLenParam != null && cutLenParam.HasValue)
            {
                return UnitUtils.ConvertFromInternalUnits(cutLenParam.AsDouble(), UnitTypeId.Meters);
            }

            if (beam.Location is LocationCurve locCurve && locCurve.Curve != null)
            {
                return UnitUtils.ConvertFromInternalUnits(locCurve.Curve.Length, UnitTypeId.Meters);
            }

            Parameter lenParam = beam.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH) ?? beam.LookupParameter("Length");
            if (lenParam != null && lenParam.HasValue)
            {
                return UnitUtils.ConvertFromInternalUnits(lenParam.AsDouble(), UnitTypeId.Meters);
            }

            return 3.0;
        }

        private double? GetParameterValueByName(Element elem, string paramName)
        {
            Parameter param = elem.LookupParameter(paramName);
            if (param == null && elem.Document.GetElement(elem.GetTypeId()) is ElementType elemType)
            {
                param = elemType.LookupParameter(paramName);
            }
            if (param != null && param.HasValue)
            {
                return UnitUtils.ConvertFromInternalUnits(param.AsDouble(), UnitTypeId.Meters);
            }
            return null;
        }

        private double GetElementZCoordinate(Element elem)
        {
            BoundingBoxXYZ box = elem.get_BoundingBox(null);
            if (box != null)
            {
                return UnitUtils.ConvertFromInternalUnits((box.Min.Z + box.Max.Z) / 2.0, UnitTypeId.Meters);
            }
            return 0.0;
        }
    }
}