using System.ComponentModel;
using CutList.Core.Formatting;
using ModelContextProtocol.Server;

namespace CutList.Mcp;

/// <summary>
/// MCP tools for inventory management - suppliers, materials, stock items, and offerings.
/// All calls go through the CutList.Web REST API via ApiClient.
/// </summary>
[McpServerToolType]
public class InventoryTools
{
    private readonly ApiClient _api;

    public InventoryTools(ApiClient api)
    {
        _api = api;
    }

    #region Suppliers

    [McpServerTool(Name = "list_suppliers"), Description("Lists all suppliers in the system.")]
    public async Task<SupplierListResult> ListSuppliers(
        [Description("Include inactive suppliers (default false)")]
        bool includeInactive = false)
    {
        var suppliers = await _api.GetSuppliersAsync(includeInactive);

        return new SupplierListResult
        {
            Success = true,
            Suppliers = suppliers.Select(s => new SupplierDto
            {
                Id = s.Id,
                Name = s.Name,
                ContactInfo = s.ContactInfo,
                Notes = s.Notes,
                IsActive = s.IsActive
            }).ToList()
        };
    }

    [McpServerTool(Name = "add_supplier"), Description("Adds a new supplier to the system.")]
    public async Task<SupplierResult> AddSupplier(
        [Description("Supplier name (e.g., 'O'Neal Steel')")]
        string name,
        [Description("Contact info - website, phone, email, etc.")]
        string? contactInfo = null,
        [Description("Notes about the supplier")]
        string? notes = null)
    {
        var supplier = await _api.CreateSupplierAsync(name, contactInfo, notes);

        return new SupplierResult
        {
            Success = true,
            Supplier = supplier != null ? new SupplierDto
            {
                Id = supplier.Id,
                Name = supplier.Name,
                ContactInfo = supplier.ContactInfo,
                Notes = supplier.Notes,
                IsActive = supplier.IsActive
            } : null
        };
    }

    #endregion

    #region Materials

    [McpServerTool(Name = "list_materials"), Description("Lists all materials (shape/size combinations) in the system.")]
    public async Task<MaterialListResult> ListMaterials(
        [Description("Filter by shape (e.g., 'Angle', 'FlatBar', 'RoundTube')")]
        string? shape = null,
        [Description("Include inactive materials (default false)")]
        bool includeInactive = false)
    {
        var materials = await _api.GetMaterialsAsync(shape, includeInactive);

        return new MaterialListResult
        {
            Success = true,
            Materials = materials.Select(MapMaterial).ToList()
        };
    }

    [McpServerTool(Name = "add_material"), Description("Adds a new material (shape/size combination) to the system with optional parsed dimensions.")]
    public async Task<MaterialResult> AddMaterial(
        [Description("Material shape (e.g., 'Angle', 'FlatBar', 'RoundTube', 'SquareTube', 'Channel', 'IBeam', 'Pipe')")]
        string shape,
        [Description("Material size string (e.g., '2 x 2 x 1/4'). If not provided, will be auto-generated from dimensions.")]
        string? size = null,
        [Description("Optional description")]
        string? description = null,
        [Description("Diameter in inches (for Round Bar)")]
        double? diameter = null,
        [Description("Outer diameter in inches (for Round Tube)")]
        double? outerDiameter = null,
        [Description("Width in inches (for Flat Bar, Rectangular Tube)")]
        double? width = null,
        [Description("Height in inches (for Rectangular Tube, Channel, I-Beam)")]
        double? height = null,
        [Description("Size in inches (for Square Bar, Square Tube - the side length)")]
        double? squareSize = null,
        [Description("Thickness in inches (for Flat Bar, Angle)")]
        double? thickness = null,
        [Description("Wall thickness in inches (for tubes, pipe)")]
        double? wall = null,
        [Description("Leg 1 length in inches (for Angle)")]
        double? leg1 = null,
        [Description("Leg 2 length in inches (for Angle)")]
        double? leg2 = null,
        [Description("Flange width in inches (for Channel)")]
        double? flange = null,
        [Description("Web thickness in inches (for Channel)")]
        double? web = null,
        [Description("Weight per foot in lbs (for I-Beam)")]
        double? weightPerFoot = null,
        [Description("Nominal pipe size in inches (for Pipe)")]
        double? nominalSize = null,
        [Description("Schedule (for Pipe, e.g., '40', '80', 'STD')")]
        string? schedule = null)
    {
        // Build dimensions dictionary from individual parameters
        var dimensions = new Dictionary<string, decimal>();
        if (diameter.HasValue) dimensions["Diameter"] = (decimal)diameter.Value;
        if (outerDiameter.HasValue) dimensions["OuterDiameter"] = (decimal)outerDiameter.Value;
        if (width.HasValue) dimensions["Width"] = (decimal)width.Value;
        if (height.HasValue) dimensions["Height"] = (decimal)height.Value;
        if (squareSize.HasValue) dimensions["Size"] = (decimal)squareSize.Value;
        if (thickness.HasValue) dimensions["Thickness"] = (decimal)thickness.Value;
        if (wall.HasValue) dimensions["Wall"] = (decimal)wall.Value;
        if (leg1.HasValue) dimensions["Leg1"] = (decimal)leg1.Value;
        if (leg2.HasValue) dimensions["Leg2"] = (decimal)leg2.Value;
        if (flange.HasValue) dimensions["Flange"] = (decimal)flange.Value;
        if (web.HasValue) dimensions["Web"] = (decimal)web.Value;
        if (weightPerFoot.HasValue) dimensions["WeightPerFoot"] = (decimal)weightPerFoot.Value;
        if (nominalSize.HasValue) dimensions["NominalSize"] = (decimal)nominalSize.Value;

        try
        {
            var material = await _api.CreateMaterialAsync(
                shape, size, description, null, null,
                dimensions.Count > 0 ? dimensions : null);

            if (material == null)
                return new MaterialResult { Success = false, Error = "Failed to create material" };

            return new MaterialResult
            {
                Success = true,
                Material = MapMaterial(material)
            };
        }
        catch (ApiConflictException ex)
        {
            return new MaterialResult { Success = false, Error = ex.Message };
        }
        catch (HttpRequestException ex)
        {
            return new MaterialResult { Success = false, Error = ex.Message };
        }
    }

    [McpServerTool(Name = "search_materials"), Description("Search for materials by shape with a target dimension value and tolerance. The primary dimension for each shape is searched (e.g., diameter for RoundBar, leg size for Angle).")]
    public async Task<MaterialListResult> SearchMaterials(
        [Description("Shape to search (e.g., 'RoundBar', 'Angle', 'FlatBar')")]
        string shape,
        [Description("Target dimension value in inches (or lbs for weightPerFoot)")]
        double targetValue,
        [Description("Tolerance value - returns results within +/- this amount (default 0.1)")]
        double tolerance = 0.1)
    {
        var materials = await _api.SearchMaterialsAsync(shape, (decimal)targetValue, (decimal)tolerance);

        return new MaterialListResult
        {
            Success = true,
            Materials = materials.Select(MapMaterial).ToList()
        };
    }

    #endregion

    #region Stock Items

    [McpServerTool(Name = "list_stock_items"), Description("Lists stock items (material lengths available in inventory).")]
    public async Task<StockItemListResult> ListStockItems(
        [Description("Filter by material ID")]
        int? materialId = null,
        [Description("Filter by shape (e.g., 'Angle')")]
        string? shape = null,
        [Description("Include inactive stock items (default false)")]
        bool includeInactive = false)
    {
        List<ApiStockItemDto> items;

        if (materialId.HasValue)
        {
            items = await _api.GetStockItemsAsync(materialId, includeInactive);
        }
        else if (!string.IsNullOrEmpty(shape))
        {
            // Get materials for this shape, then get stock items for each
            var materials = await _api.GetMaterialsAsync(shape, includeInactive);
            var allItems = new List<ApiStockItemDto>();
            foreach (var mat in materials)
            {
                var matItems = await _api.GetStockItemsAsync(mat.Id, includeInactive);
                allItems.AddRange(matItems);
            }
            items = allItems;
        }
        else
        {
            items = await _api.GetStockItemsAsync(includeInactive: includeInactive);
        }

        return new StockItemListResult
        {
            Success = true,
            StockItems = items.Select(s => new StockItemDto
            {
                Id = s.Id,
                MaterialId = s.MaterialId,
                MaterialName = s.MaterialName,
                LengthInches = s.LengthInches,
                LengthFormatted = s.LengthFormatted,
                Name = s.Name,
                QuantityOnHand = s.QuantityOnHand,
                Notes = s.Notes,
                IsActive = s.IsActive
            }).ToList()
        };
    }

    [McpServerTool(Name = "add_stock_item"), Description("Adds a new stock item (a specific length of material that can be stocked).")]
    public async Task<StockItemResult> AddStockItem(
        [Description("Material ID (use list_materials to find IDs)")]
        int materialId,
        [Description("Stock length (e.g., '20'', '240', '20 ft')")]
        string length,
        [Description("Optional name/label for this stock item")]
        string? name = null,
        [Description("Initial quantity on hand (default 0)")]
        int quantityOnHand = 0,
        [Description("Notes")]
        string? notes = null)
    {
        try
        {
            var stockItem = await _api.CreateStockItemAsync(materialId, length, name, quantityOnHand, notes);

            if (stockItem == null)
                return new StockItemResult { Success = false, Error = "Failed to create stock item" };

            return new StockItemResult
            {
                Success = true,
                StockItem = new StockItemDto
                {
                    Id = stockItem.Id,
                    MaterialId = stockItem.MaterialId,
                    MaterialName = stockItem.MaterialName,
                    LengthInches = stockItem.LengthInches,
                    LengthFormatted = stockItem.LengthFormatted,
                    Name = stockItem.Name,
                    QuantityOnHand = stockItem.QuantityOnHand,
                    Notes = stockItem.Notes,
                    IsActive = stockItem.IsActive
                }
            };
        }
        catch (ApiConflictException ex)
        {
            return new StockItemResult { Success = false, Error = ex.Message };
        }
        catch (HttpRequestException ex)
        {
            return new StockItemResult { Success = false, Error = ex.Message };
        }
    }

    #endregion

    #region Supplier Offerings

    [McpServerTool(Name = "list_supplier_offerings"), Description("Lists supplier offerings (what suppliers sell for each stock item).")]
    public async Task<SupplierOfferingListResult> ListSupplierOfferings(
        [Description("Filter by supplier ID")]
        int? supplierId = null,
        [Description("Filter by stock item ID")]
        int? stockItemId = null,
        [Description("Filter by material ID")]
        int? materialId = null)
    {
        List<ApiOfferingDto> offerings;

        if (supplierId.HasValue)
        {
            offerings = await _api.GetOfferingsForSupplierAsync(supplierId.Value);
            // Apply additional filters client-side
            if (stockItemId.HasValue)
                offerings = offerings.Where(o => o.StockItemId == stockItemId.Value).ToList();
            if (materialId.HasValue)
            {
                // Need to get stock items for this material to filter
                var stockItems = await _api.GetStockItemsAsync(materialId);
                var stockItemIds = stockItems.Select(s => s.Id).ToHashSet();
                offerings = offerings.Where(o => stockItemIds.Contains(o.StockItemId)).ToList();
            }
        }
        else if (stockItemId.HasValue)
        {
            offerings = await _api.GetOfferingsForStockItemAsync(stockItemId.Value);
        }
        else if (materialId.HasValue)
        {
            // Get stock items for this material, then aggregate offerings
            var stockItems = await _api.GetStockItemsAsync(materialId);
            var allOfferings = new List<ApiOfferingDto>();
            foreach (var si in stockItems)
            {
                var siOfferings = await _api.GetOfferingsForStockItemAsync(si.Id);
                allOfferings.AddRange(siOfferings);
            }
            offerings = allOfferings;
        }
        else
        {
            // No filter - get all suppliers then aggregate
            var suppliers = await _api.GetSuppliersAsync();
            var allOfferings = new List<ApiOfferingDto>();
            foreach (var s in suppliers)
            {
                var sOfferings = await _api.GetOfferingsForSupplierAsync(s.Id);
                allOfferings.AddRange(sOfferings);
            }
            offerings = allOfferings;
        }

        return new SupplierOfferingListResult
        {
            Success = true,
            Offerings = offerings.Select(o => new SupplierOfferingDto
            {
                Id = o.Id,
                SupplierId = o.SupplierId,
                SupplierName = o.SupplierName ?? string.Empty,
                StockItemId = o.StockItemId,
                MaterialName = o.MaterialName ?? string.Empty,
                LengthFormatted = o.LengthFormatted ?? string.Empty,
                PartNumber = o.PartNumber,
                SupplierDescription = o.SupplierDescription,
                Price = o.Price,
                Notes = o.Notes
            }).ToList()
        };
    }

    [McpServerTool(Name = "add_supplier_offering"), Description("Adds a supplier offering - links a supplier to a stock item with their part number and pricing.")]
    public async Task<SupplierOfferingResult> AddSupplierOffering(
        [Description("Supplier ID (use list_suppliers to find)")]
        int supplierId,
        [Description("Stock item ID (use list_stock_items to find)")]
        int stockItemId,
        [Description("Supplier's part number")]
        string? partNumber = null,
        [Description("Supplier's description of the item")]
        string? supplierDescription = null,
        [Description("Price per unit")]
        decimal? price = null,
        [Description("Notes")]
        string? notes = null)
    {
        try
        {
            var offering = await _api.CreateOfferingAsync(supplierId, stockItemId, partNumber, supplierDescription, price, notes);

            if (offering == null)
                return new SupplierOfferingResult { Success = false, Error = "Failed to create offering" };

            return new SupplierOfferingResult
            {
                Success = true,
                Offering = new SupplierOfferingDto
                {
                    Id = offering.Id,
                    SupplierId = offering.SupplierId,
                    SupplierName = offering.SupplierName ?? string.Empty,
                    StockItemId = offering.StockItemId,
                    MaterialName = offering.MaterialName ?? string.Empty,
                    LengthFormatted = offering.LengthFormatted ?? string.Empty,
                    PartNumber = offering.PartNumber,
                    SupplierDescription = offering.SupplierDescription,
                    Price = offering.Price,
                    Notes = offering.Notes
                }
            };
        }
        catch (ApiConflictException ex)
        {
            return new SupplierOfferingResult { Success = false, Error = ex.Message };
        }
        catch (HttpRequestException ex)
        {
            return new SupplierOfferingResult { Success = false, Error = ex.Message };
        }
    }

    [McpServerTool(Name = "add_stock_with_offering"), Description("Convenience method: adds a material (if needed), stock item (if needed), and supplier offering all at once.")]
    public async Task<AddStockWithOfferingResult> AddStockWithOffering(
        [Description("Supplier ID (use list_suppliers or add_supplier first)")]
        int supplierId,
        [Description("Material shape (e.g., 'Angle', 'FlatBar')")]
        string shape,
        [Description("Material size (e.g., '2 x 2 x 1/4')")]
        string size,
        [Description("Stock length (e.g., '20'', '240')")]
        string length,
        [Description("Material type: Steel, Aluminum, Stainless, Brass, Copper (default: Steel)")]
        string type = "Steel",
        [Description("Grade or specification (e.g., 'A36', 'Hot Roll', '304', '6061-T6')")]
        string? grade = null,
        [Description("Supplier's part number")]
        string? partNumber = null,
        [Description("Supplier's description")]
        string? supplierDescription = null,
        [Description("Price per unit")]
        decimal? price = null)
    {
        // Parse length for formatted display
        double lengthInches;
        try
        {
            lengthInches = double.TryParse(length.Trim(), out var plain)
                ? plain
                : ArchUnits.ParseToInches(length);
        }
        catch
        {
            return new AddStockWithOfferingResult
            {
                Success = false,
                Error = $"Could not parse length: {length}"
            };
        }

        // Step 1: Find or create material
        bool materialCreated = false;
        ApiMaterialDto? material = null;

        // Search for existing material by shape and size
        var materials = await _api.GetMaterialsAsync(shape);
        material = materials.FirstOrDefault(m =>
            m.Size.Equals(size, StringComparison.OrdinalIgnoreCase) &&
            m.Type.Equals(type, StringComparison.OrdinalIgnoreCase) &&
            string.Equals(m.Grade, grade, StringComparison.OrdinalIgnoreCase));

        if (material == null)
        {
            // Parse dimensions from size string for the API
            var dimensions = ParseSizeStringToDimensions(shape, size);

            try
            {
                material = await _api.CreateMaterialAsync(shape, size, null, type, grade, dimensions);
                materialCreated = true;
            }
            catch (ApiConflictException)
            {
                // Race condition - material was created between check and create, re-fetch
                materials = await _api.GetMaterialsAsync(shape);
                material = materials.FirstOrDefault(m =>
                    m.Size.Equals(size, StringComparison.OrdinalIgnoreCase));
            }
            catch (HttpRequestException ex)
            {
                return new AddStockWithOfferingResult { Success = false, Error = $"Failed to create material: {ex.Message}" };
            }
        }

        if (material == null)
            return new AddStockWithOfferingResult { Success = false, Error = "Failed to find or create material" };

        // Step 2: Find or create stock item
        bool stockItemCreated = false;
        var stockItems = await _api.GetStockItemsAsync(material.Id);
        var stockItem = stockItems.FirstOrDefault(s => Math.Abs((double)s.LengthInches - lengthInches) < 0.01);

        if (stockItem == null)
        {
            try
            {
                stockItem = await _api.CreateStockItemAsync(material.Id, length, null, 0, null);
                stockItemCreated = true;
            }
            catch (ApiConflictException)
            {
                // Race condition - re-fetch
                stockItems = await _api.GetStockItemsAsync(material.Id);
                stockItem = stockItems.FirstOrDefault(s => Math.Abs((double)s.LengthInches - lengthInches) < 0.01);
            }
            catch (HttpRequestException ex)
            {
                return new AddStockWithOfferingResult
                {
                    Success = false,
                    Error = $"Failed to create stock item: {ex.Message}",
                    MaterialCreated = materialCreated
                };
            }
        }

        if (stockItem == null)
            return new AddStockWithOfferingResult
            {
                Success = false,
                Error = "Failed to find or create stock item",
                MaterialCreated = materialCreated
            };

        // Step 3: Create offering
        try
        {
            var offering = await _api.CreateOfferingAsync(supplierId, stockItem.Id, partNumber, supplierDescription, price, null);

            return new AddStockWithOfferingResult
            {
                Success = true,
                MaterialId = material.Id,
                MaterialName = $"{material.Shape} - {material.Size}",
                MaterialCreated = materialCreated,
                StockItemId = stockItem.Id,
                StockItemCreated = stockItemCreated,
                LengthFormatted = ArchUnits.FormatFromInches(lengthInches),
                OfferingId = offering?.Id ?? 0,
                PartNumber = partNumber,
                SupplierDescription = supplierDescription,
                Price = price
            };
        }
        catch (ApiConflictException)
        {
            return new AddStockWithOfferingResult
            {
                Success = false,
                Error = $"Offering for this supplier and stock item already exists",
                MaterialCreated = materialCreated,
                StockItemCreated = stockItemCreated
            };
        }
        catch (HttpRequestException ex)
        {
            return new AddStockWithOfferingResult
            {
                Success = false,
                Error = $"Failed to create offering: {ex.Message}",
                MaterialCreated = materialCreated,
                StockItemCreated = stockItemCreated
            };
        }
    }

    #endregion

    #region Helpers

    private static MaterialDto MapMaterial(ApiMaterialDto m)
    {
        var dto = new MaterialDto
        {
            Id = m.Id,
            Shape = m.Shape,
            Size = m.Size,
            Description = m.Description,
            DisplayName = $"{m.Shape} - {m.Size}",
            IsActive = m.IsActive,
        };

        if (m.Dimensions != null)
        {
            dto.Dimensions = MapDimensions(m.Dimensions);
        }

        return dto;
    }

    private static MaterialDimensionsDto MapDimensions(ApiMaterialDimensionsDto d)
    {
        var dto = new MaterialDimensionsDto();
        var v = d.Values;

        if (v.TryGetValue("Diameter", out var diameter)) dto.Diameter = (double)diameter;
        if (v.TryGetValue("OuterDiameter", out var od)) dto.OuterDiameter = (double)od;
        if (v.TryGetValue("Width", out var width)) dto.Width = (double)width;
        if (v.TryGetValue("Height", out var height)) dto.Height = (double)height;
        if (v.TryGetValue("Size", out var size)) dto.Size = (double)size;
        if (v.TryGetValue("Thickness", out var thickness)) dto.Thickness = (double)thickness;
        if (v.TryGetValue("Wall", out var wall)) dto.Wall = (double)wall;
        if (v.TryGetValue("Leg1", out var leg1)) dto.Leg1 = (double)leg1;
        if (v.TryGetValue("Leg2", out var leg2)) dto.Leg2 = (double)leg2;
        if (v.TryGetValue("Flange", out var flange)) dto.Flange = (double)flange;
        if (v.TryGetValue("Web", out var web)) dto.Web = (double)web;
        if (v.TryGetValue("WeightPerFoot", out var wpf)) dto.WeightPerFoot = (double)wpf;
        if (v.TryGetValue("NominalSize", out var ns)) dto.NominalSize = (double)ns;

        return dto;
    }

    /// <summary>
    /// Parses a size string into a dimensions dictionary based on shape.
    /// Format: values separated by 'x' (e.g., "1 1/2 x 1/8", "2 x 2 x 1/4")
    /// </summary>
    private static Dictionary<string, decimal>? ParseSizeStringToDimensions(string shape, string sizeString)
    {
        var parts = sizeString.Split('x', StringSplitOptions.TrimEntries)
            .Select(ParseDimension)
            .ToArray();

        if (parts.Length == 0 || parts.All(p => !p.HasValue))
            return null;

        decimal D(int i) => (decimal)parts[i]!.Value;

        var shapeLower = shape.ToLowerInvariant().Replace(" ", "");

        return shapeLower switch
        {
            "roundbar" when parts.Length >= 1 && parts[0].HasValue
                => new Dictionary<string, decimal> { ["Diameter"] = D(0) },

            "roundtube" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
                => new Dictionary<string, decimal> { ["OuterDiameter"] = D(0), ["Wall"] = D(1) },

            "flatbar" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
                => new Dictionary<string, decimal> { ["Width"] = D(0), ["Thickness"] = D(1) },

            "squarebar" when parts.Length >= 1 && parts[0].HasValue
                => new Dictionary<string, decimal> { ["Size"] = D(0) },

            "squaretube" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
                => new Dictionary<string, decimal> { ["Size"] = D(0), ["Wall"] = D(1) },

            "rectangulartube" or "recttube" when parts.Length >= 3 && parts[0].HasValue && parts[1].HasValue && parts[2].HasValue
                => new Dictionary<string, decimal> { ["Width"] = D(0), ["Height"] = D(1), ["Wall"] = D(2) },

            "angle" when parts.Length >= 3 && parts[0].HasValue && parts[1].HasValue && parts[2].HasValue
                => new Dictionary<string, decimal> { ["Leg1"] = D(0), ["Leg2"] = D(1), ["Thickness"] = D(2) },

            "channel" when parts.Length >= 3 && parts[0].HasValue && parts[1].HasValue && parts[2].HasValue
                => new Dictionary<string, decimal> { ["Height"] = D(0), ["Flange"] = D(1), ["Web"] = D(2) },

            "ibeam" when parts.Length >= 2 && parts[0].HasValue && parts[1].HasValue
                => new Dictionary<string, decimal> { ["Height"] = D(0), ["WeightPerFoot"] = D(1) },

            "pipe" when parts.Length >= 1 && parts[0].HasValue
                => new Dictionary<string, decimal> { ["NominalSize"] = D(0) },

            _ => null
        };

        static double? ParseDimension(string value)
        {
            if (string.IsNullOrWhiteSpace(value)) return null;
            var processed = Fraction.ReplaceFractionsWithDecimals(value.Trim());
            return processed.Split(' ', StringSplitOptions.RemoveEmptyEntries)
                .Sum(part => double.TryParse(part, out var d) ? d : 0) is > 0 and var total ? total : null;
        }
    }

    #endregion
}

#region DTOs

public class SupplierDto
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string? ContactInfo { get; set; }
    public string? Notes { get; set; }
    public bool IsActive { get; set; }
}

public class SupplierListResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public List<SupplierDto> Suppliers { get; set; } = new();
}

public class SupplierResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public SupplierDto? Supplier { get; set; }
}

public class MaterialDimensionsDto
{
    public double? Diameter { get; set; }
    public double? OuterDiameter { get; set; }
    public double? Width { get; set; }
    public double? Height { get; set; }
    public double? Size { get; set; }
    public double? Thickness { get; set; }
    public double? Wall { get; set; }
    public double? Leg1 { get; set; }
    public double? Leg2 { get; set; }
    public double? Flange { get; set; }
    public double? Web { get; set; }
    public double? WeightPerFoot { get; set; }
    public double? NominalSize { get; set; }
    public string? Schedule { get; set; }
}

public class MaterialDto
{
    public int Id { get; set; }
    public string Shape { get; set; } = string.Empty;
    public string Size { get; set; } = string.Empty;
    public string? Description { get; set; }
    public string DisplayName { get; set; } = string.Empty;
    public bool IsActive { get; set; }
    public MaterialDimensionsDto? Dimensions { get; set; }
}

public class MaterialListResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public List<MaterialDto> Materials { get; set; } = new();
}

public class MaterialResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public MaterialDto? Material { get; set; }
}

public class StockItemDto
{
    public int Id { get; set; }
    public int MaterialId { get; set; }
    public string MaterialName { get; set; } = string.Empty;
    public decimal LengthInches { get; set; }
    public string LengthFormatted { get; set; } = string.Empty;
    public string? Name { get; set; }
    public int QuantityOnHand { get; set; }
    public string? Notes { get; set; }
    public bool IsActive { get; set; }
}

public class StockItemListResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public List<StockItemDto> StockItems { get; set; } = new();
}

public class StockItemResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public StockItemDto? StockItem { get; set; }
}

public class SupplierOfferingDto
{
    public int Id { get; set; }
    public int SupplierId { get; set; }
    public string SupplierName { get; set; } = string.Empty;
    public int StockItemId { get; set; }
    public string MaterialName { get; set; } = string.Empty;
    public string LengthFormatted { get; set; } = string.Empty;
    public string? PartNumber { get; set; }
    public string? SupplierDescription { get; set; }
    public decimal? Price { get; set; }
    public string? Notes { get; set; }
}

public class SupplierOfferingListResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public List<SupplierOfferingDto> Offerings { get; set; } = new();
}

public class SupplierOfferingResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public SupplierOfferingDto? Offering { get; set; }
}

public class AddStockWithOfferingResult
{
    public bool Success { get; set; }
    public string? Error { get; set; }
    public int MaterialId { get; set; }
    public string MaterialName { get; set; } = string.Empty;
    public bool MaterialCreated { get; set; }
    public int StockItemId { get; set; }
    public bool StockItemCreated { get; set; }
    public string LengthFormatted { get; set; } = string.Empty;
    public int OfferingId { get; set; }
    public string? PartNumber { get; set; }
    public string? SupplierDescription { get; set; }
    public decimal? Price { get; set; }
}

#endregion
