using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Plumbing;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;
using Microsoft.Win32;

namespace RevitEstimate.PlumbingModule
{
    public partial class PlumbingEstimateWindow : Window
    {
        private Document _doc;
        public List<FixtureConfig> FixturesSetup { get; set; }
        public List<RoomFixtureData> RoomMatrix { get; set; }
        private CollectionViewSource _roomViewSource;
        private CollectionViewSource _dsrViewSource;
        private List<DsrEstimateItem> _currentDsrItems;

        public PlumbingEstimateWindow(Document doc)
        {
            InitializeComponent();
            _doc = doc;
            LoadRevitDataToUI();
            InitializePipeDropdowns();
            LoadBimModelQuantities();
        }

        private void InitializePipeDropdowns()
        {
            var wsMat = new[] { "CPVC", "PPR", "UPVC", "HDPE", "GI", "Copper", "Stainless Steel", "DI", "MS" };
            CmbWS_MainMat.ItemsSource = CmbWS_BranchMat.ItemsSource = CmbWS_DistMat.ItemsSource = wsMat;
            CmbWS_MainDia.ItemsSource = new[] { "32 mm", "40 mm", "50 mm" };
            CmbWS_BranchDia.ItemsSource = new[] { "20 mm", "25 mm", "32 mm" };
            CmbWS_DistDia.ItemsSource = new[] { "15 mm", "20 mm" };
            SetDefault(CmbWS_MainMat, 0); SetDefault(CmbWS_MainDia, 2); SetDefault(CmbWS_BranchMat, 0); SetDefault(CmbWS_BranchDia, 1); SetDefault(CmbWS_DistMat, 0); SetDefault(CmbWS_DistDia, 0);

            var sanMat = new[] { "uPVC SWR", "Cast Iron (CI)", "HDPE", "PP" };
            CmbSan_MainMat.ItemsSource = CmbSan_BranchMat.ItemsSource = CmbSan_DistMat.ItemsSource = sanMat;
            CmbSan_MainDia.ItemsSource = new[] { "110 mm", "160 mm" };
            CmbSan_BranchDia.ItemsSource = new[] { "75 mm", "90 mm", "110 mm" };
            CmbSan_DistDia.ItemsSource = new[] { "40 mm", "50 mm", "75 mm", "90 mm", "110 mm" };
            SetDefault(CmbSan_MainMat, 0); SetDefault(CmbSan_MainDia, 0); SetDefault(CmbSan_BranchMat, 0); SetDefault(CmbSan_BranchDia, 0); SetDefault(CmbSan_DistMat, 0); SetDefault(CmbSan_DistDia, 4);

            var wwMat = new[] { "uPVC SWR", "Cast Iron (CI)", "HDPE", "PP", "Stoneware/Vitrified Clay" };
            CmbWW_MainMat.ItemsSource = CmbWW_BranchMat.ItemsSource = CmbWW_DistMat.ItemsSource = wwMat;
            CmbWW_MainDia.ItemsSource = new[] { "110 mm", "160 mm" };
            CmbWW_BranchDia.ItemsSource = new[] { "75 mm", "90 mm", "110 mm" };
            CmbWW_DistDia.ItemsSource = new[] { "40 mm", "50 mm", "75 mm" };
            SetDefault(CmbWW_MainMat, 0); SetDefault(CmbWW_MainDia, 0); SetDefault(CmbWW_BranchMat, 0); SetDefault(CmbWW_BranchDia, 0); SetDefault(CmbWW_DistMat, 0); SetDefault(CmbWW_DistDia, 1);

            var rwMat = new[] { "uPVC SWR", "HDPE", "Cast Iron", "RCC", "GRP/FRP", "Stoneware/Vitrified Clay" };
            CmbRW_MainMat.ItemsSource = CmbRW_BranchMat.ItemsSource = CmbRW_DistMat.ItemsSource = rwMat;
            CmbRW_MainDia.ItemsSource = new[] { "110 mm", "160 mm" };
            CmbRW_BranchDia.ItemsSource = new[] { "75 mm", "90 mm", "110 mm" };
            CmbRW_DistDia.ItemsSource = new[] { "75 mm", "90 mm", "110 mm" };
            SetDefault(CmbRW_MainMat, 0); SetDefault(CmbRW_MainDia, 0); SetDefault(CmbRW_BranchMat, 0); SetDefault(CmbRW_BranchDia, 0); SetDefault(CmbRW_DistMat, 0); SetDefault(CmbRW_DistDia, 0);
        }

        private void SetDefault(ComboBox cmb, int index) { if (cmb.Items.Count > index) cmb.SelectedIndex = index; }

        private void LoadRevitDataToUI()
        {
            var levels = new FilteredElementCollector(_doc).OfClass(typeof(Level)).Cast<Level>().OrderBy(l => l.Elevation).ToList();
            CmbTankLevel.ItemsSource = levels;
            if (levels.Count > 0) CmbTankLevel.SelectedIndex = levels.Count - 1;

            FixturesSetup = new List<FixtureConfig>
            {
                new FixtureConfig { FixtureName = "Wash Basin (WB Point)", HeightFromFFL = 0.60 },
                new FixtureConfig { FixtureName = "Health Faucet (HF)", HeightFromFFL = 0.38 },
                new FixtureConfig { FixtureName = "Geyser Inlet / Outlet", HeightFromFFL = 2.00 },
                new FixtureConfig { FixtureName = "Kitchen Sink / Lab Sink", HeightFromFFL = 0.90 },
                new FixtureConfig { FixtureName = "Shower Head", HeightFromFFL = 2.00 }
            };
            GridFixturesConfig.ItemsSource = FixturesSetup;

            var floors = new FilteredElementCollector(_doc).OfCategory(BuiltInCategory.OST_Floors).WhereElementIsNotElementType().Cast<Floor>().ToList();
            Dictionary<ElementId, double> levelSlabAreas = new Dictionary<ElementId, double>();
            foreach (var floor in floors)
            {
                ElementId levelId = floor.LevelId;
                double areaInt = floor.get_Parameter(BuiltInParameter.HOST_AREA_COMPUTED)?.AsDouble() ?? 0.0;
                double areaSqM = UnitUtils.ConvertFromInternalUnits(areaInt, UnitTypeId.SquareMeters);
                if (levelSlabAreas.ContainsKey(levelId)) levelSlabAreas[levelId] += areaSqM;
                else levelSlabAreas[levelId] = areaSqM;
            }

            var rooms = new FilteredElementCollector(_doc).OfCategory(BuiltInCategory.OST_Rooms).WhereElementIsNotElementType().Cast<Room>().Where(r => r.Area > 0).ToList();
            RoomMatrix = new List<RoomFixtureData>();
            var roomsByLevel = rooms.GroupBy(r => r.LevelId);

            foreach (var levelGroup in roomsByLevel)
            {
                Level roomLevel = _doc.GetElement(levelGroup.Key) as Level;
                string levelName = roomLevel != null ? roomLevel.Name : "Unassigned";
                double elevMeters = roomLevel != null ? UnitUtils.ConvertFromInternalUnits(roomLevel.Elevation, UnitTypeId.Meters) : 0.0;
                double levelSlabArea = (roomLevel != null && levelSlabAreas.ContainsKey(roomLevel.Id))
                    ? levelSlabAreas[roomLevel.Id]
                    : levelGroup.Sum(r => UnitUtils.ConvertFromInternalUnits(r.Area, UnitTypeId.SquareMeters));

                var sortedRooms = levelGroup.OrderBy(r => { var bb = r.get_BoundingBox(null); return bb != null ? (bb.Min.X + bb.Max.X) / 2 : 0; }).ToList();

                int flatCounter = 1; double lastX = -999999;
                foreach (var room in sortedRooms)
                {
                    var bb = room.get_BoundingBox(null);
                    double roomX = bb != null ? UnitUtils.ConvertFromInternalUnits((bb.Min.X + bb.Max.X) / 2, UnitTypeId.Meters) : 0;
                    double roomY = bb != null ? UnitUtils.ConvertFromInternalUnits((bb.Min.Y + bb.Max.Y) / 2, UnitTypeId.Meters) : 0;

                    if (lastX != -999999 && Math.Abs(roomX - lastX) > 12.0) flatCounter++;
                    lastX = roomX;

                    double perimeterMeters = UnitUtils.ConvertFromInternalUnits(room.Perimeter, UnitTypeId.Meters);
                    string nameLower = room.Name.ToLower();
                    bool isToilet = nameLower.Contains("toilet") || nameLower.Contains("bath") || nameLower.Contains("wc") || nameLower.Contains("powder");
                    bool isKitchen = nameLower.Contains("kitchen");
                    bool isUtility = nameLower.Contains("utility") || nameLower.Contains("wash");
                    bool isBalcony = nameLower.Contains("balcony") || nameLower.Contains("stilt") || nameLower.Contains("open");
                    bool isWet = isToilet || isKitchen || isUtility;
                    bool isSemiWet = isBalcony;

                    RoomMatrix.Add(new RoomFixtureData
                    {
                        LevelName = levelName,
                        FlatNumber = flatCounter,
                        IsFlatEnabled = true,
                        RoomTag = $"{room.Name} - {room.Number}",
                        RoomName = room.Name,
                        ZoneType = isWet ? "Wet Room" : (isSemiWet ? "Semi-Wet / Open" : "Dry Room"),
                        SortOrder = isWet ? 1 : (isSemiWet ? 2 : 3),
                        IsRoomActive = isWet || isSemiWet,
                        FloorElevation = elevMeters,
                        RoomPerimeter = perimeterMeters,
                        RoomArea = levelSlabArea,
                        RoomCenterX = roomX,
                        RoomCenterY = roomY,
                        WashBasinQty = (isToilet || isUtility) ? 1 : 0,
                        WaterClosetQty = isToilet ? 1 : 0,
                        HealthFaucetQty = isToilet ? 1 : 0,
                        KitchenSinkQty = isKitchen ? 1 : 0,
                        ShowerQty = isToilet ? 1 : 0,
                        GeyserQty = isToilet ? 1 : 0,
                        WashingMachineQty = (isWet || isSemiWet) ? 1 : 0
                    });
                }
            }

            var sortedList = RoomMatrix.OrderBy(r => r.FloorElevation).ThenBy(r => r.FlatNumber).ThenBy(r => r.SortOrder).ToList();
            _roomViewSource = new CollectionViewSource { Source = sortedList };
            _roomViewSource.GroupDescriptions.Add(new PropertyGroupDescription("FlatHeader"));
            GridRoomMatrix.ItemsSource = _roomViewSource.View;
        }

        private void LoadBimModelQuantities()
        {
            var bimItems = new List<BimElementQuantity>();

            // 1. Read from Revit Pipe Schedules (ViewSchedule)
            var schedules = new FilteredElementCollector(_doc).OfClass(typeof(ViewSchedule)).Cast<ViewSchedule>().ToList();
            foreach (var schedule in schedules)
            {
                if (schedule.Name.ToLower().Contains("pipe") || schedule.Name.ToLower().Contains("plumbing"))
                {
                    TableData tableData = schedule.GetTableData();
                    TableSectionData bodySection = tableData?.GetSectionData(SectionType.Body);
                    if (bodySection != null && bodySection.NumberOfRows > 1)
                    {
                        for (int r = 1; r < bodySection.NumberOfRows; r++)
                        {
                            string typeName = bodySection.GetCellText(r, 0);
                            string lengthText = "";
                            for (int c = 0; c < bodySection.NumberOfColumns; c++)
                            {
                                string cellVal = bodySection.GetCellText(r, c);
                                if (cellVal.ToLower().Contains("m") || double.TryParse(cellVal, out _))
                                {
                                    lengthText = cellVal;
                                }
                            }

                            if (!string.IsNullOrEmpty(typeName))
                            {
                                double qty = 0.0;
                                double.TryParse(System.Text.RegularExpressions.Regex.Match(lengthText, @"\d+[\.]?\d*").Value, out qty);

                                bimItems.Add(new BimElementQuantity
                                {
                                    Category = "Pipe Schedule",
                                    FamilyName = schedule.Name,
                                    TypeName = typeName,
                                    SystemType = "Schedule Import",
                                    Quantity = qty > 0 ? qty : 1.0,
                                    Unit = "Metres/Nos",
                                    Level = "From Schedule"
                                });
                            }
                        }
                    }
                }
            }

            // 2. Direct Model Extraction for Pipes
            var pipes = new FilteredElementCollector(_doc).OfClass(typeof(Pipe)).Cast<Pipe>().ToList();
            var groupedPipes = pipes.GroupBy(p => new {
                TypeName = p.Name,
                LevelName = p.ReferenceLevel?.Name ?? "Unassigned"
            });

            foreach (var group in groupedPipes)
            {
                double totalLenMeters = group.Sum(p => {
                    double internalLen = 0.0;
                    LocationCurve locCurve = p.Location as LocationCurve;
                    if (locCurve != null && locCurve.Curve != null)
                    {
                        internalLen = locCurve.Curve.Length;
                    }
                    return UnitUtils.ConvertFromInternalUnits(internalLen, UnitTypeId.Meters);
                });

                var samplePipe = group.First();
                string familyName = samplePipe.Document.GetElement(samplePipe.GetTypeId())?.Name ?? "Pipe Family";

                bimItems.Add(new BimElementQuantity
                {
                    Category = "Pipes (Model)",
                    FamilyName = familyName,
                    TypeName = group.Key.TypeName,
                    SystemType = "Plumbing / MEP",
                    Quantity = totalLenMeters,
                    Unit = "Metres",
                    Level = group.Key.LevelName
                });
            }

            // Extract Plumbing Fixtures
            var fixtures = new FilteredElementCollector(_doc).OfCategory(BuiltInCategory.OST_PlumbingFixtures).WhereElementIsNotElementType().Cast<FamilyInstance>().ToList();
            var groupedFixtures = fixtures.GroupBy(f => new {
                FamName = f.Symbol.FamilyName,
                TypeName = f.Symbol.Name,
                LevelName = _doc.GetElement(f.LevelId)?.Name ?? "Unassigned"
            });

            foreach (var group in groupedFixtures)
            {
                bimItems.Add(new BimElementQuantity
                {
                    Category = "Plumbing Fixtures",
                    FamilyName = group.Key.FamName,
                    TypeName = group.Key.TypeName,
                    SystemType = "Sanitary / Fixture",
                    Quantity = group.Count(),
                    Unit = "Nos",
                    Level = group.Key.LevelName
                });
            }

            // Extract Mechanical Equipment
            var equipment = new FilteredElementCollector(_doc).OfCategory(BuiltInCategory.OST_MechanicalEquipment).WhereElementIsNotElementType().Cast<FamilyInstance>().ToList();
            var groupedEq = equipment.GroupBy(e => new {
                FamName = e.Symbol.FamilyName,
                TypeName = e.Symbol.Name,
                LevelName = _doc.GetElement(e.LevelId)?.Name ?? "Unassigned"
            });

            foreach (var group in groupedEq)
            {
                bimItems.Add(new BimElementQuantity
                {
                    Category = "Mechanical Equipment",
                    FamilyName = group.Key.FamName,
                    TypeName = group.Key.TypeName,
                    SystemType = "Tank / Equipment",
                    Quantity = group.Count(),
                    Unit = "Nos",
                    Level = group.Key.LevelName
                });
            }

            GridBimQuantities.ItemsSource = bimItems;
        }

        private void FlatToggle_Click(object sender, RoutedEventArgs e)
        {
            if (sender is CheckBox chk && chk.DataContext is CollectionViewGroup group)
            {
                bool isChecked = chk.IsChecked ?? false;
                foreach (RoomFixtureData item in group.Items) item.IsFlatEnabled = isChecked;
                GridRoomMatrix.Items.Refresh();
            }
        }

        private void BtnGenerateReport_Click(object sender, RoutedEventArgs e)
        {
            var activeRooms = RoomMatrix.Where(r => r.IsFlatEnabled && r.IsRoomActive).ToList();
            Level selectedTankLevel = CmbTankLevel.SelectedItem as Level;
            double tankElev = selectedTankLevel != null ? UnitUtils.ConvertFromInternalUnits(selectedTankLevel.Elevation, UnitTypeId.Meters) : 0.0;
            foreach (var r in activeRooms) r.TankElevation = tankElev;

            var pipeConfig = new PipeSelectionConfig
            {
                WS_MainMat = CmbWS_MainMat.Text,
                WS_MainDia = CmbWS_MainDia.Text,
                WS_BranchMat = CmbWS_BranchMat.Text,
                WS_BranchDia = CmbWS_BranchDia.Text,
                WS_DistMat = CmbWS_DistMat.Text,
                WS_DistDia = CmbWS_DistDia.Text,

                San_MainMat = CmbSan_MainMat.Text,
                San_MainDia = CmbSan_MainDia.Text,
                San_BranchMat = CmbSan_BranchMat.Text,
                San_BranchDia = CmbSan_BranchDia.Text,
                San_DistMat = CmbSan_DistMat.Text,
                San_DistDia = CmbSan_DistDia.Text,

                WW_MainMat = CmbWW_MainMat.Text,
                WW_MainDia = CmbWW_MainDia.Text,
                WW_BranchMat = CmbWW_BranchMat.Text,
                WW_BranchDia = CmbWW_BranchDia.Text,
                WW_DistMat = CmbWW_DistMat.Text,
                WW_DistDia = CmbWW_DistDia.Text,

                RW_MainMat = CmbRW_MainMat.Text,
                RW_MainDia = CmbRW_MainDia.Text,
                RW_BranchMat = CmbRW_BranchMat.Text,
                RW_BranchDia = CmbRW_BranchDia.Text,
                RW_DistMat = CmbRW_DistMat.Text,
                RW_DistDia = CmbRW_DistDia.Text
            };

            _currentDsrItems = PlumbingDsrCalculator.GenerateEstimate(activeRooms, pipeConfig);

            _dsrViewSource = new CollectionViewSource { Source = _currentDsrItems };
            _dsrViewSource.GroupDescriptions.Add(new PropertyGroupDescription("CategoryGroup"));
            GridDsrEstimate.ItemsSource = _dsrViewSource.View;

            double totalAmount = _currentDsrItems.Sum(item => item.Amount);
            TxtTotalAmount.Text = totalAmount.ToString("N2");

            MainTabControl.SelectedIndex = 4;
        }

        private void BtnExportDsrExcel_Click(object sender, RoutedEventArgs e)
        {
            if (_currentDsrItems == null || _currentDsrItems.Count == 0)
            {
                MessageBox.Show("No DSR estimate data found to export! Please generate the estimate first.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            SaveFileDialog saveDialog = new SaveFileDialog
            {
                Filter = "Excel Workbook (*.xls)|*.xls|CSV File (*.csv)|*.csv",
                FileName = "MEP_Plumbing_DSR_Estimate.xls",
                Title = "Select Destination Folder and File Name"
            };

            if (saveDialog.ShowDialog() == true)
            {
                if (saveDialog.FileName.EndsWith(".xls"))
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendLine("<html xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:x=\"urn:schemas-microsoft-com:office:excel\" xmlns=\"http://www.w3.org/TR/REC-html40\">");
                    sb.AppendLine("<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><style>");
                    sb.AppendLine("table { border-collapse: collapse; width: 100%; font-family: Calibri, sans-serif; }");
                    sb.AppendLine("th { background-color: #007ACC; color: white; border: 1px solid #000000; padding: 8px; text-align: center; font-weight: bold; }");
                    sb.AppendLine("td { border: 1px solid #cccccc; padding: 6px; font-size: 11pt; }");
                    sb.AppendLine("</style></head>");
                    sb.AppendLine("<body>");
                    sb.AppendLine("<h2>Advanced MEP Plumbing DSR Estimation &amp; Measurement Sheet</h2>");
                    sb.AppendLine("<table>");
                    sb.AppendLine("<tr><th>Group Category</th><th>Sr. No.</th><th>DSR Code</th><th>Description of Item</th><th>Logic / Remark</th><th>Unit</th><th>Quantity</th><th>Rate (₹)</th><th>Amount (₹)</th></tr>");

                    foreach (var item in _currentDsrItems)
                    {
                        sb.AppendLine($"<tr>");
                        sb.AppendLine($"<td>{item.CategoryGroup}</td>");
                        sb.AppendLine($"<td style=\"text-align:center;\">{item.SrNo}</td>");
                        sb.AppendLine($"<td style=\"text-align:center;\">{item.DsrCode}</td>");
                        sb.AppendLine($"<td>{item.Description}</td>");
                        sb.AppendLine($"<td style=\"color:#555555; font-style:italic;\">{item.Remark}</td>");
                        sb.AppendLine($"<td style=\"text-align:center;\">{item.Unit}</td>");
                        sb.AppendLine($"<td style=\"text-align:right;\">{item.Qty:N2}</td>");
                        sb.AppendLine($"<td style=\"text-align:right;\">{item.Rate:N2}</td>");
                        sb.AppendLine($"<td style=\"text-align:right; font-weight:bold;\">{item.Amount:N2}</td>");
                        sb.AppendLine($"</tr>");
                    }

                    sb.AppendLine("</table>");
                    sb.AppendLine("</body></html>");

                    File.WriteAllText(saveDialog.FileName, sb.ToString(), Encoding.UTF8);
                }
                else
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendLine("Group Category,Sr. No.,DSR Code,Description,Remark,Unit,Qty,Rate,Amount");
                    foreach (var item in _currentDsrItems)
                    {
                        string desc = $"\"{item.Description.Replace("\"", "\"\"")}\"";
                        string rmk = $"\"{item.Remark?.Replace("\"", "\"\"") ?? ""}\"";
                        sb.AppendLine($"\"{item.CategoryGroup}\",{item.SrNo},{item.DsrCode},{desc},{rmk},{item.Unit},{item.Qty},{item.Rate},{item.Amount}");
                    }
                    File.WriteAllText(saveDialog.FileName, sb.ToString(), Encoding.UTF8);
                }

                MessageBox.Show("Estimate successfully exported to your chosen folder!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
                try { System.Diagnostics.Process.Start(saveDialog.FileName); } catch { }
            }
        }
    }
}