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

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

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

        private class FloorLevelInfo
        {
            public string Name { get; set; }
            public double BottomZ { get; set; }
            public double TopZ { get; set; }
        }

        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;

            var activeFloors = new List<FloorLevelInfo>();
            activeFloors.Add(new FloorLevelInfo { Name = "GF", BottomZ = _levels.TopOfPlinth, TopZ = _levels.SlabTop });
            double currentTop = _levels.SlabTop;

            if (_levels.FirstFloorSlabTop != -8888 && _levels.FirstFloorSlabTop != 0)
            {
                activeFloors.Add(new FloorLevelInfo { Name = "FF", BottomZ = currentTop, TopZ = _levels.FirstFloorSlabTop });
                currentTop = _levels.FirstFloorSlabTop;
            }
            if (_levels.SecondFloorSlabTop != -8888 && _levels.SecondFloorSlabTop != 0)
            {
                activeFloors.Add(new FloorLevelInfo { Name = "SF", BottomZ = currentTop, TopZ = _levels.SecondFloorSlabTop });
                currentTop = _levels.SecondFloorSlabTop;
            }
            if (_levels.ThirdFloorSlabTop != -8888 && _levels.ThirdFloorSlabTop != 0)
            {
                activeFloors.Add(new FloorLevelInfo { Name = "TF", BottomZ = currentTop, TopZ = _levels.ThirdFloorSlabTop });
                currentTop = _levels.ThirdFloorSlabTop;
            }
            if (_levels.TerraceRoofTop != -8888 && _levels.TerraceRoofTop != 0)
            {
                activeFloors.Add(new FloorLevelInfo { Name = "Terrace", BottomZ = currentTop, TopZ = _levels.TerraceRoofTop });
            }

            // ==========================================
            // 1. CALCULATE SKIRTING FROM ROOMS
            // ==========================================
            var rooms = collector.Where(e => e is Room).Cast<Room>().ToList();
            if (rooms.Count == 0)
            {
                rooms = new FilteredElementCollector(doc)
                    .OfCategory(BuiltInCategory.OST_Rooms)
                    .Cast<Room>()
                    .ToList();
            }

            double skirtingHeight = 0.10;

            foreach (var room in rooms)
            {
                string roomName = room.Name ?? "";
                string roomLower = roomName.ToLower();

                if (roomLower.Contains("open") || roomLower.Contains("stilt") || roomLower.Contains("courtyard") || roomLower.Contains("parking"))
                {
                    double roomZ = room.Level != null ? UnitUtils.ConvertFromInternalUnits(room.Level.Elevation, UnitTypeId.Meters) : 0;
                    var floor = activeFloors.FirstOrDefault(f => roomZ >= (f.BottomZ - 0.5) && roomZ <= (f.TopZ + 0.5)) ?? activeFloors.First();

                    double area = UnitUtils.ConvertFromInternalUnits(room.Area, UnitTypeId.SquareMeters);
                    if (area <= 0) continue;

                    double side = Math.Sqrt(area);
                    double length = Math.Round(side * 1.1, 2);
                    double breadth = Math.Round(area / length, 2);

                    double qtyL = 2.0 * length * skirtingHeight;
                    double qtyB = 2.0 * breadth * skirtingHeight;

                    totalQty += (qtyL + qtyB);

                    rows.Add(new EstimateRowModel
                    {
                        SNo = "",
                        ItemOfWork = $"   -> Skirting (Length sides) in {roomName} ({floor.Name}) [ID: {room.Id}]",
                        NosOfFlats = "1",
                        NosOfInstance = "2",
                        Length = length.ToString("F2"),
                        Width = "",
                        HeightDepth = skirtingHeight.ToString("F2"),
                        Qty = qtyL.ToString("F2"),
                        Unit = "Sqm",
                        Remarks = $"Skirting: 2 x L ({length}m) x H ({skirtingHeight}m)."
                    });

                    rows.Add(new EstimateRowModel
                    {
                        SNo = "",
                        ItemOfWork = $"   -> Skirting (Breadth sides) in {roomName} ({floor.Name}) [ID: {room.Id}]",
                        NosOfFlats = "1",
                        NosOfInstance = "2",
                        Length = breadth.ToString("F2"),
                        Width = "",
                        HeightDepth = skirtingHeight.ToString("F2"),
                        Qty = qtyB.ToString("F2"),
                        Unit = "Sqm",
                        Remarks = $"Skirting: 2 x B ({breadth}m) x H ({skirtingHeight}m)."
                    });
                }
            }

            // ==========================================
            // 2. CALCULATE KOTA STONE FINISH ON STAIRS
            // ==========================================
            var stairs = collector
                .Where(e => e.Category != null && e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Stairs)
                .ToList();

            foreach (var stair in stairs)
            {
                string stairName = stair.Name;
                ElementId stairId = stair.Id;

                int actualRisers = GetParameterInt(stair, "Actual Risers") > 0 ? GetParameterInt(stair, "Actual Risers") : 15;
                int actualTreads = GetParameterInt(stair, "Actual Treads") > 0 ? GetParameterInt(stair, "Actual Treads") : 14;
                double riserHeight = GetParameterDouble(stair, "Actual Riser Height") > 0 ? GetParameterDouble(stair, "Actual Riser Height") : 0.15;
                double treadDepth = GetParameterDouble(stair, "Actual Tread Depth") > 0 ? GetParameterDouble(stair, "Actual Tread Depth") : 0.25;
                double stairWidth = GetParameterDouble(stair, "Width") > 0 ? GetParameterDouble(stair, "Width") : 1.20;

                double qtyTreads = actualTreads * treadDepth * stairWidth;
                totalQty += qtyTreads;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Kota Stone Finish on Treads for Stair: {stairName} [ID: {stairId}]",
                    NosOfFlats = "1",
                    NosOfInstance = actualTreads.ToString(),
                    Length = treadDepth.ToString("F2"),
                    Width = stairWidth.ToString("F2"),
                    HeightDepth = "",
                    Qty = qtyTreads.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Stair treads finishing ({actualTreads} treads x Depth={treadDepth}m x Width={stairWidth}m)."
                });

                double qtyRisers = actualRisers * riserHeight * stairWidth;
                totalQty += qtyRisers;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Kota Stone Finish on Risers for Stair: {stairName} [ID: {stairId}]",
                    NosOfFlats = "1",
                    NosOfInstance = actualRisers.ToString(),
                    Length = stairWidth.ToString("F2"),
                    Width = "",
                    HeightDepth = riserHeight.ToString("F2"),
                    Qty = qtyRisers.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Stair risers finishing ({actualRisers} risers x Height={riserHeight}m x Width={stairWidth}m)."
                });

                double landingArea = GetLandingArea(stair);
                totalQty += landingArea;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Kota Stone Finish on Landing for Stair: {stairName} [ID: {stairId}]",
                    NosOfFlats = "1",
                    NosOfInstance = "1",
                    Length = landingArea.ToString("F2"),
                    Width = "",
                    HeightDepth = "",
                    Qty = landingArea.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Stair landing stone finishing area ({landingArea:F2} Sqm)."
                });
            }

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

            return rows;
        }

        private int GetParameterInt(Element elem, string paramName)
        {
            Parameter param = elem.LookupParameter(paramName);
            if (param != null && param.HasValue) return param.AsInteger();
            return 0;
        }

        private double GetParameterDouble(Element elem, string paramName)
        {
            Parameter param = elem.LookupParameter(paramName);
            if (param != null && param.HasValue)
            {
                return UnitUtils.ConvertFromInternalUnits(param.AsDouble(), UnitTypeId.Meters);
            }
            return 0.0;
        }

        private double GetLandingArea(Element stair)
        {
            try
            {
                BoundingBoxXYZ box = stair.get_BoundingBox(null);
                if (box != null)
                {
                    double width = UnitUtils.ConvertFromInternalUnits(box.Max.X - box.Min.X, UnitTypeId.Meters);
                    double length = UnitUtils.ConvertFromInternalUnits(box.Max.Y - box.Min.Y, UnitTypeId.Meters);
                    if (width > 0 && length > 0)
                    {
                        return Math.Round(width * length * 0.3, 2);
                    }
                }
            }
            catch { }
            return 1.5;
        }
    }
}