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

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

        public Item52Calculator(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 FLOORS / SLABS
            // ==========================================
            var floors = collector
                .Where(e => e.Category != null && e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Floors)
                .Cast<Floor>()
                .ToList();

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

            // Sort floors by Elevation (Z coordinate) in descending order to identify top-most slabs
            var sortedFloors = floors
                .Select(f => new { Floor = f, Z = GetFloorElevation(doc, f) })
                .OrderByDescending(x => x.Z)
                .ToList();

            // We need Mumty level slab and the slab just below it (Top 2 highest slabs)
            var targetSlabs = sortedFloors.Take(2).ToList();

            foreach (var item in targetSlabs)
            {
                Floor floor = item.Floor;
                Parameter areaParam = floor.get_Parameter(BuiltInParameter.HOST_AREA_COMPUTED) ?? floor.LookupParameter("Area");
                double area = areaParam != null && areaParam.HasValue ? UnitUtils.ConvertFromInternalUnits(areaParam.AsDouble(), UnitTypeId.SquareMeters) : 0.0;

                if (area <= 0) continue;

                totalQty += area;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Waterproofing (Brick bat coba) on Terrace/Mumty Slab: {floor.Name} [ID: {floor.Id}]",
                    NosOfFlats = "1",
                    NosOfInstance = "1",
                    Length = area.ToString("F2"),
                    Width = "",
                    HeightDepth = "",
                    Qty = area.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Waterproofing treatment restricted to Mumty level & top-most slab (Z={item.Z:F2}m)."
                });
            }

            if (rows.Count == 0)
            {
                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = "   -> [No valid terrace/mumty slabs found for Item 52]",
                    NosOfFlats = "-",
                    NosOfInstance = "0",
                    Length = "-",
                    Width = "-",
                    HeightDepth = "-",
                    Qty = "0.00",
                    Unit = "Sqm",
                    Remarks = "No matching slabs detected."
                });
            }

            return rows;
        }

        private double GetFloorElevation(Document doc, Floor floor)
        {
            // Try Bounding Box first
            BoundingBoxXYZ box = floor.get_BoundingBox(null);
            if (box != null)
            {
                return UnitUtils.ConvertFromInternalUnits(box.Min.Z, UnitTypeId.Meters);
            }

            // Fallback to Level lookup via LevelId
            ElementId levelId = floor.LevelId;
            if (levelId != null && levelId != ElementId.InvalidElementId)
            {
                Level lvl = doc.GetElement(levelId) as Level;
                if (lvl != null)
                {
                    return UnitUtils.ConvertFromInternalUnits(lvl.Elevation, UnitTypeId.Meters);
                }
            }

            return 0.0;
        }
    }
}