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

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

        public Item13Calculator(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 Walls in the model
            var walls = collector.Where(e => e.Category != null && e.Category.Id.IntegerValue == (int)BuiltInCategory.OST_Walls).Cast<Wall>().ToList();

            // Height = Bottom of Plinth (Manual/Custom input from UI, e.g. 1.57) minus Bottom of Foundation (Same previous logic)
            double bottomPlinth = _levels.BottomOfPlinth != 0 ? _levels.BottomOfPlinth : 1.57;
            double bottomFoundation = _levels.BottomOfFoundation;
            double wallHeight = Math.Abs(bottomPlinth - bottomFoundation);
            if (wallHeight <= 0) wallHeight = 1.57; // Default fallback

            foreach (var wall in walls)
            {
                // 1. Check if wall thickness is 230mm (0.23m) - Outer walls
                double wallThickness = GetWallThickness(wall);
                if (Math.Abs(wallThickness - 0.23) > 0.02) continue;

                string wallName = wall.Name;
                double cutLength = GetWallLength(wall);
                if (cutLength <= 0) cutLength = 3.0;

                double qty = cutLength * wallThickness * wallHeight; // Length x Thickness x Height (Cum)
                totalQty += qty;

                rows.Add(new EstimateRowModel
                {
                    SNo = "",
                    ItemOfWork = $"   -> Outer Wall (Item 13): {wallName} [ID: {wall.Id}]",
                    NosOfFlats = "1",
                    NosOfInstance = "1",
                    Length = cutLength.ToString("F2"),
                    Width = wallThickness.ToString("F2"), // Thickness
                    HeightDepth = wallHeight.ToString("F2"), // Same height/depth logic
                    Qty = qty.ToString("F2"),
                    Unit = "Cum",
                    Remarks = $"230mm outer wall work with previous height logic up to bottom of plinth ({wallHeight:F2}m)."
                });
            }

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

            return rows;
        }

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

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

            return 3.0;
        }

        private double GetWallThickness(Wall wall)
        {
            WallType wallType = wall.WallType;
            if (wallType != null)
            {
                CompoundStructure cs = wallType.GetCompoundStructure();
                if (cs != null)
                {
                    return UnitUtils.ConvertFromInternalUnits(cs.GetWidth(), UnitTypeId.Meters);
                }

                Parameter widthParam = wallType.LookupParameter("Width");
                if (widthParam != null && widthParam.HasValue)
                {
                    return UnitUtils.ConvertFromInternalUnits(widthParam.AsDouble(), UnitTypeId.Meters);
                }
            }

            Parameter instWidth = wall.LookupParameter("Width");
            if (instWidth != null && instWidth.HasValue)
            {
                return UnitUtils.ConvertFromInternalUnits(instWidth.AsDouble(), UnitTypeId.Meters);
            }

            return 0.23;
        }
    }
}