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 Item27Calculator : IItemCalculator
    {
        private ProjectLevels _levels;

        public Item27Calculator(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;

            // ==========================================
            // SETUP ACTIVE FLOORS FROM LEVEL WINDOW
            // ==========================================
            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 });
            }

            // ==========================================
            // COLLECT 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();
            }

            // Collect doors for deductions in skirting
            var doors = collector
                .Where(e => e is FamilyInstance && e.Category != null && e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Doors)
                .Cast<FamilyInstance>().ToList();

            double skirtingHeight = 0.10; // 10 cm skirting height

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

                // EXCLUDE: Open Area, Stilt, Bathrooms, Toilets, Kitchens, Powder Rooms, Parking, Courtyard
                bool isExcluded = roomLower.Contains("open") ||
                                  roomLower.Contains("stilt") ||
                                  roomLower.Contains("courtyard") ||
                                  roomLower.Contains("parking") ||
                                  roomLower.Contains("bath") ||
                                  roomLower.Contains("toilet") ||
                                  roomLower.Contains("wc") ||
                                  roomLower.Contains("kitchen") ||
                                  roomLower.Contains("powder");

                if (isExcluded) continue;

                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;

                // 1. Flooring Area Entry (With proper [ID: ...] for Smart Naming)
                totalQty += area;
                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Vitrified Tiles Flooring in {roomName} ({floor.Name}) [ID: {room.Id}]",
                    NosOfFlats = "1",
                    NosOfInstance = "1",
                    Length = area.ToString("F2"),
                    Width = "",
                    HeightDepth = "",
                    Qty = area.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Vitrified tiles flooring area from room tag '{roomName}'."
                });

                // Calculate L and B for skirting
                double side = Math.Sqrt(area);
                double length = Math.Round(side * 1.1, 2);
                double breadth = Math.Round(area / length, 2);

                // 2. Skirting Length Sides (2 x L x H)
                double qtyL = 2.0 * length * skirtingHeight;
                totalQty += qtyL;
                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Skirting (Length sides) in {roomName} ({floor.Name})",
                    NosOfFlats = "1",
                    NosOfInstance = "2",
                    Length = length.ToString("F2"),
                    Width = "",
                    HeightDepth = skirtingHeight.ToString("F2"),
                    Qty = qtyL.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Skirting area: 2 x L ({length}m) x H ({skirtingHeight}m)."
                });

                // 3. Skirting Breadth Sides (2 x B x H)
                double qtyB = 2.0 * breadth * skirtingHeight;
                totalQty += qtyB;
                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Skirting (Breadth sides) in {roomName} ({floor.Name})",
                    NosOfFlats = "1",
                    NosOfInstance = "2",
                    Length = breadth.ToString("F2"),
                    Width = "",
                    HeightDepth = skirtingHeight.ToString("F2"),
                    Qty = qtyB.ToString("F2"),
                    Unit = "Sqm",
                    Remarks = $"Skirting area: 2 x B ({breadth}m) x H ({skirtingHeight}m)."
                });

                // 4. Deductions for doors connected to this room (With proper [ID: ...] for Smart Naming)
                foreach (var door in doors)
                {
                    if (door.ToRoom != null && door.ToRoom.Id == room.Id || door.FromRoom != null && door.FromRoom.Id == room.Id)
                    {
                        double doorWidth = GetDimensionFromTypeOrInstance(door, "Width");
                        if (doorWidth == 0) doorWidth = 1.0;

                        double deductQty = -1.0 * (doorWidth * skirtingHeight);
                        totalQty += deductQty;

                        rows.Add(new EstimateRowModel
                        {
                            SNo = "",
                            ItemOfWork = $"   -> Deduct Skirting for Door: {door.Name} [ID: {door.Id}]",
                            NosOfFlats = "",
                            NosOfInstance = "1",
                            Length = doorWidth.ToString("F2"),
                            Width = "",
                            HeightDepth = skirtingHeight.ToString("F2"),
                            Qty = deductQty.ToString("F2"),
                            Unit = "Sqm",
                            Remarks = $"Skirting deduction for door opening width ({doorWidth}m)."
                        });
                    }
                }
            }

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

            return rows;
        }

        private double GetDimensionFromTypeOrInstance(FamilyInstance inst, string paramName)
        {
            if (inst.Symbol != null) { Parameter typeParam = inst.Symbol.LookupParameter(paramName); if (typeParam != null && typeParam.HasValue) return UnitUtils.ConvertFromInternalUnits(typeParam.AsDouble(), UnitTypeId.Meters); }
            Parameter instParam = inst.LookupParameter(paramName);
            if (instParam != null && instParam.HasValue) return UnitUtils.ConvertFromInternalUnits(instParam.AsDouble(), UnitTypeId.Meters);
            return 0;
        }
    }
}