using System.Text;
using System.Text.Json;
using Anthropic.SDK;
using Anthropic.SDK.Messaging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using PowderCoating.Application.DTOs.AI;
using PowderCoating.Application.Interfaces;
using PowderCoating.Core.Entities;

namespace PowderCoating.Infrastructure.Services;

public class AiQuickQuoteService : IAiQuickQuoteService
{
    private readonly IConfiguration _config;
    private readonly ILogger<AiQuickQuoteService> _logger;

    /// <summary>
    /// Lean system prompt focused on verbal/phone descriptions.
    /// Intentionally shorter than the photo-analysis prompt — no image guidance needed.
    /// The detectedColorNames field lets Claude extract powder names so the server can
    /// resolve inventory stock without sending the full inventory to the model.
    /// </summary>
    private const string SystemPrompt = @"You are an expert powder coating estimator. A customer is describing items over the phone or at the shop counter.

Based on the verbal description, respond ONLY with a valid JSON object — no markdown, no explanation:

{
  ""description"": ""string - concise item name (e.g., 'Steel bracket set', 'Aluminum wheel rims x4')"",
  ""surfaceAreaSqFt"": number - estimated surface area per single item in square feet,
  ""complexity"": ""Simple"" | ""Moderate"" | ""Complex"" | ""Extreme"",
  ""estimatedMinutes"": number - estimated ACTIVE LABOR time in minutes per item (blasting, masking, application, inspection — NOT oven cure time),
  ""requiresPreheat"": boolean - true for cast iron, cast aluminum, galvanized steel, wrought iron,
  ""preheatMinutes"": number - 0 if requiresPreheat is false; typical: cast iron 45-60, cast aluminum 30-45, galvanized 30-45,
  ""confidence"": ""Low"" | ""Medium"" | ""High"",
  ""reasoning"": ""string - one sentence explaining key assumptions made"",
  ""detectedColorNames"": [""string""] - color or powder finish names mentioned by the customer verbatim (e.g., [""Matte Black"", ""Alien Silver""]); empty array if none mentioned
}

Complexity guide:
- Simple: flat panels, basic shapes, minimal masking
- Moderate: moderate curves, some recesses, standard masking
- Complex: intricate geometry, deep recesses, welded assemblies, significant masking
- Extreme: highly ornate, deep cavities, heavy prep and masking required

When estimating from a verbal description:
- Default to steel if material is not stated
- Use common reference sizes (car wheel ~1.5-2.5 sqft, motorcycle frame ~8-12 sqft, door ~20 sqft, railing section ~4-6 sqft)
- Set confidence to ""Low"" when dimensions are missing or vague
- Never ask follow-up questions — return your best estimate with Low confidence instead";

    public AiQuickQuoteService(IConfiguration config, ILogger<AiQuickQuoteService> logger)
    {
        _config = config;
        _logger = logger;
    }

    /// <inheritdoc/>
    public async Task<AiQuickQuoteResult> AnalyzeAsync(
        AiQuickQuoteRequest request,
        CompanyOperatingCosts costs,
        decimal avgPowderCostPerLb,
        CompanyAiContext? context = null)
    {
        var apiKey = _config["AI:Anthropic:ApiKey"];
        if (string.IsNullOrWhiteSpace(apiKey) || apiKey.StartsWith("your-"))
        {
            return new AiQuickQuoteResult
            {
                Success = false,
                ErrorMessage = "Anthropic API key is not configured. Contact your administrator."
            };
        }

        try
        {
            var client = new AnthropicClient(apiKey);

            var messageRequest = new MessageParameters
            {
                Model = "claude-sonnet-4-6",
                MaxTokens = 512,
                Temperature = 0.2m,
                SystemMessage = BuildSystemPrompt(context),
                Messages = new List<Message>
                {
                    new Message
                    {
                        Role = RoleType.User,
                        Content = new List<ContentBase>
                        {
                            new TextContent { Text = BuildUserPrompt(request) }
                        }
                    }
                }
            };

            using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
            var response = await client.Messages.GetClaudeMessageAsync(messageRequest, cts.Token);
            var rawText = response.FirstMessage?.Text
                ?? response.Content.OfType<TextContent>().FirstOrDefault()?.Text
                ?? "";

            _logger.LogInformation("Claude quick quote response: {Response}",
                rawText.Length > 300 ? rawText[..300] : rawText);

            var claudeResult = ParseResponse(rawText);
            if (claudeResult == null)
            {
                return new AiQuickQuoteResult
                {
                    Success = false,
                    ErrorMessage = "AI returned an unexpected response format. Please try again."
                };
            }

            var (unitPrice, total, breakdown) = CalculatePricing(claudeResult, request, costs, avgPowderCostPerLb);

            return new AiQuickQuoteResult
            {
                Success = true,
                Description = claudeResult.Description,
                SurfaceAreaSqFt = claudeResult.SurfaceAreaSqFt,
                Complexity = claudeResult.Complexity,
                EstimatedMinutes = claudeResult.EstimatedMinutes,
                RequiresPreheat = claudeResult.RequiresPreheat,
                PreheatMinutes = claudeResult.PreheatMinutes,
                Confidence = claudeResult.Confidence,
                Reasoning = claudeResult.Reasoning,
                // Controller fills in stock status for each entry after an inventory lookup
                PowderMatches = claudeResult.DetectedColorNames
                    .Select(n => new PowderStockMatch { DetectedColorName = n })
                    .ToList(),
                EstimatedUnitPrice = unitPrice,
                EstimatedTotal = total,
                Breakdown = breakdown
            };
        }
        catch (OperationCanceledException)
        {
            _logger.LogWarning("Quick quote AI request timed out after 30 s");
            return new AiQuickQuoteResult { Success = false, ErrorMessage = "Request timed out. Please try again." };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Quick quote AI analysis failed");
            return new AiQuickQuoteResult { Success = false, ErrorMessage = "AI analysis failed. Please try again." };
        }
    }

    private static string BuildSystemPrompt(CompanyAiContext? context)
    {
        if (context == null ||
            (string.IsNullOrWhiteSpace(context.ProfileText) && context.AcceptedExamples.Count == 0))
            return SystemPrompt;

        var sb = new StringBuilder(SystemPrompt);

        if (!string.IsNullOrWhiteSpace(context.ProfileText))
        {
            sb.AppendLine();
            sb.AppendLine();
            sb.AppendLine("COMPANY-SPECIFIC CONTEXT — use this to calibrate estimates for this shop:");
            sb.AppendLine(context.ProfileText.Trim());
        }

        if (context.AcceptedExamples.Count > 0)
        {
            sb.AppendLine();
            sb.AppendLine("CALIBRATION EXAMPLES from this shop's accepted quotes:");
            foreach (var ex in context.AcceptedExamples)
                sb.AppendLine($"- {ex.Description}: {ex.SurfaceAreaSqFt:F1} sqft, {ex.Complexity}, {ex.EstimatedMinutes} min, ${ex.FinalUnitPrice:F2}/item");
        }

        return sb.ToString();
    }

    private static string BuildUserPrompt(AiQuickQuoteRequest request)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"Customer description: {request.Description}");
        sb.AppendLine($"Quantity: {request.Quantity}");
        sb.AppendLine($"Coat count requested: {request.CoatCount}");
        return sb.ToString().TrimEnd();
    }

    private ClaudeQuickQuoteResponse? ParseResponse(string rawText)
    {
        try
        {
            var json = rawText.Trim();
            if (json.StartsWith("```"))
            {
                var start = json.IndexOf('{');
                var end = json.LastIndexOf('}');
                if (start >= 0 && end > start)
                    json = json[start..(end + 1)];
            }

            return JsonSerializer.Deserialize<ClaudeQuickQuoteResponse>(json,
                new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "Failed to parse Claude quick quote JSON: {Raw}", rawText);
            return null;
        }
    }

    /// <summary>
    /// Server-side pricing using the same math as AiQuoteService.
    /// Material-type minimum floors are omitted — verbal descriptions rarely include material,
    /// and the confidence returned by the AI already reflects that uncertainty.
    /// </summary>
    private static (decimal UnitPrice, decimal Total, AiPricingBreakdown Breakdown) CalculatePricing(
        ClaudeQuickQuoteResponse ai,
        AiQuickQuoteRequest request,
        CompanyOperatingCosts costs,
        decimal avgPowderCostPerLb)
    {
        const decimal defaultCoverage = 30m;
        const decimal defaultEfficiency = 0.65m;

        var lbsPerCoat = ai.SurfaceAreaSqFt > 0
            ? ai.SurfaceAreaSqFt / (defaultCoverage * defaultEfficiency)
            : 0m;
        var materialCost = lbsPerCoat * request.CoatCount * avgPowderCostPerLb;
        var consumablesSurcharge = materialCost * 0.05m;

        // System prompt asks Claude for per-item minutes — no division by quantity needed.
        var perItemMinutes = (decimal)ai.EstimatedMinutes;
        var laborCost = (perItemMinutes / 60m) * costs.StandardLaborRate;

        var preheatCost = 0m;
        var preheatMinutes = 0;
        if (ai.RequiresPreheat && ai.PreheatMinutes > 0)
        {
            preheatMinutes = ai.PreheatMinutes;
            preheatCost = (preheatMinutes / 60m) * costs.OvenOperatingCostPerHour;
        }

        var materialWithMarkup = (materialCost + consumablesSurcharge) * (1 + costs.GeneralMarkupPercentage / 100m);
        var subtotalBeforeComplexity = materialWithMarkup + laborCost + preheatCost;

        var complexityPct = ai.Complexity switch
        {
            "Simple"   => costs.ComplexitySimplePercent / 100m,
            "Moderate" => costs.ComplexityModeratePercent / 100m,
            "Complex"  => costs.ComplexityComplexPercent / 100m,
            "Extreme"  => costs.ComplexityExtremePercent / 100m,
            _          => 0m
        };
        var complexityCharge = subtotalBeforeComplexity * complexityPct;
        var subtotal = subtotalBeforeComplexity + complexityCharge;

        if (subtotal < costs.ShopMinimumCharge && costs.ShopMinimumCharge > 0)
            subtotal = costs.ShopMinimumCharge;

        var unitPrice = Math.Max(0, Math.Round(subtotal, 2));
        var markupAmount = (materialCost + consumablesSurcharge) * (costs.GeneralMarkupPercentage / 100m);

        // Oven batch charge: 1 batch, DefaultOvenCycleMinutes (fallback 50 min).
        // Added at quote level (not baked into unitPrice) to match how the regular pricing engine works.
        var ovenCycleMinutes = costs.DefaultOvenCycleMinutes > 0 ? costs.DefaultOvenCycleMinutes : 50;
        var ovenBatchCost = Math.Round((ovenCycleMinutes / 60m) * costs.OvenOperatingCostPerHour, 2);

        var total = unitPrice * request.Quantity + ovenBatchCost;

        var breakdown = new AiPricingBreakdown
        {
            SurfaceAreaSqFt          = Math.Round(ai.SurfaceAreaSqFt, 2),
            PowderLbsPerCoat         = Math.Round(lbsPerCoat, 3),
            CoatCount                = request.CoatCount,
            MaterialCost             = Math.Round(materialCost, 2),
            ConsumablesCost          = Math.Round(consumablesSurcharge, 2),
            EstimatedMinutes         = ai.EstimatedMinutes,
            MaterialMinMinutes       = 0,
            MinFloorApplied          = false,
            LaborCost                = Math.Round(laborCost, 2),
            OvenCycleMinutes         = ovenCycleMinutes,
            OvenCost                 = ovenBatchCost,
            RequiresPreheat          = ai.RequiresPreheat,
            PreheatMinutes           = preheatMinutes,
            PreheatCost              = Math.Round(preheatCost, 2),
            SubtotalBeforeComplexity = Math.Round(subtotalBeforeComplexity, 2),
            Complexity               = ai.Complexity,
            ComplexityCharge         = Math.Round(complexityCharge, 2),
            SubtotalBeforeMarkup     = Math.Round(subtotalBeforeComplexity, 2),
            MarkupAmount             = Math.Round(markupAmount, 2),
            UnitPrice                = unitPrice
        };

        return (unitPrice, total, breakdown);
    }
}
