using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Caching.Memory;
using Altob.NtuInvoiceGateway.Models;
using System.Text;
using System.Text.Json;
using System.Linq;
using System.Globalization;

namespace Altob.NtuInvoiceGateway.Pages;

[IgnoreAntiforgeryToken]  // 允許外部系統 POST JSON 請求
public class InvoiceModel : PageModel
{
    private readonly ILogger<InvoiceModel> _logger;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IMemoryCache _memoryCache;
    private readonly CompanyInfo _companyInfo;
    private readonly EndpointApiOptions _endpointApiOptions;
    private readonly bool _debugMode;
    private const string ServiceName = "NtuInvoiceGateway";
    private const string DonateCodeCacheKey = "DonateCodeList";
    private const string InvoiceDataTempKey = "InvoiceFormData";

    public InvoiceModel(
        ILogger<InvoiceModel> logger,
        IHttpClientFactory httpClientFactory,
        IMemoryCache memoryCache,
        IOptions<CompanyInfo> companyInfo,
        IOptions<EndpointApiOptions> endpointApiOptions,
        IConfiguration configuration)
    {
        _logger = logger;
        _httpClientFactory = httpClientFactory;
        _memoryCache = memoryCache;
        _companyInfo = companyInfo.Value;
        _endpointApiOptions = endpointApiOptions.Value;
        _debugMode = configuration.GetValue<bool>("DebugMode");
    }

    [BindProperty(SupportsGet = true)]
    public InvoiceRequest InvoiceData { get; set; } = new();

    public string? ErrorMessage { get; set; }
    [TempData]
    public string? SuccessMessage { get; set; }
    public string CompanyName => _companyInfo.Name;
    public string CompanyTaxId => _companyInfo.TaxId;

    // 顯示用的發票資訊（唯讀）
    public string DisplayTransDateTime { get; set; } = string.Empty;
    public string DisplayTransAmount { get; set; } = string.Empty;

    public void OnGet()
    {
        RestoreInvoiceDataFromTempData();
        RestoreDisplayValuesFromTempData();
        NormalizeInvoiceData();
        // 如果有 GET 參數，設置顯示資訊
        DisplayTransDateTime = InvoiceData.TransDateTime;
        DisplayTransAmount = InvoiceData.TransAmount;

        if (!Request.Query.Any())
        {
            ModelState.Clear();
        }
    }

    public async Task<IActionResult> OnGetDonateCodesAsync()
    {
        var actionName = nameof(OnGetDonateCodesAsync);

        try
        {
            // 檢查 Cache 是否有資料
            if (_memoryCache.TryGetValue(DonateCodeCacheKey, out List<DonateGroup>? cachedData) && cachedData != null)
            {
                _logger.LogInformation("{ServiceName} - {ActionName} returning cached donate codes, count: {Count}",
                    ServiceName, actionName, cachedData.Count);
                return new JsonResult(cachedData);
            }

            // Cache 中沒有資料，從 API 取得
            if (string.IsNullOrWhiteSpace(_endpointApiOptions.DonateCodeApi))
            {
                _logger.LogError("{ServiceName} - {ActionName} DonateCode API endpoint is not configured", ServiceName, actionName);
                return new JsonResult(new List<DonateGroup>()) { StatusCode = 500 };
            }

            _logger.LogInformation("{ServiceName} - {ActionName} fetching donate codes from API: {Endpoint}",
                ServiceName, actionName, _endpointApiOptions.DonateCodeApi);

            var httpClient = _httpClientFactory.CreateClient();
            var response = await httpClient.GetAsync(_endpointApiOptions.DonateCodeApi);

            if (!response.IsSuccessStatusCode)
            {
                _logger.LogError("{ServiceName} - {ActionName} failed to fetch donate codes, status: {StatusCode}",
                    ServiceName, actionName, response.StatusCode);
                return new JsonResult(new List<DonateGroup>()) { StatusCode = (int)response.StatusCode };
            }

            var responseContent = await response.Content.ReadAsStringAsync();
            var donateCodes = JsonSerializer.Deserialize<List<DonateGroup>>(responseContent, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            });

            if (donateCodes != null && donateCodes.Count > 0)
            {
                // 按照 Seq 欄位排序
                var sortedDonateCodes = donateCodes.OrderBy(d => d.Seq).ToList();

                // 將資料存入 Cache，有效期限 7 天
                var cacheOptions = new MemoryCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(7)
                };
                _memoryCache.Set(DonateCodeCacheKey, sortedDonateCodes, cacheOptions);

                _logger.LogInformation("{ServiceName} - {ActionName} cached {Count} donate codes for 7 days",
                    ServiceName, actionName, sortedDonateCodes.Count);

                return new JsonResult(sortedDonateCodes);
            }

            _logger.LogWarning("{ServiceName} - {ActionName} received empty donate code list from API", ServiceName, actionName);
            return new JsonResult(new List<DonateGroup>());
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "{ServiceName} - {ActionName} error fetching donate codes", ServiceName, actionName);
            return new JsonResult(new List<DonateGroup>()) { StatusCode = 500 };
        }
    }

    public async Task<IActionResult> OnPostAsync()
    {
        NormalizeInvoiceData();
        var actionName = nameof(OnPostAsync);
        _logger.LogInformation("{ServiceName} - {ActionName} received JSON redirect payload {@InvoiceData}",
            ServiceName, actionName, InvoiceData);
        // 檢查是否為 JSON 請求（外部系統轉頁）
        if (Request.ContentType?.Contains("application/json") == true)
        {
            try
            {
                // 從 Request Body 讀取 JSON 資料
                using var reader = new StreamReader(Request.Body);
                var jsonContent = await reader.ReadToEndAsync();
                _logger.LogInformation("{ServiceName} - {ActionName} JSON payload content: {JsonPayload}", ServiceName, actionName, jsonContent);

                var jsonData = JsonSerializer.Deserialize<InvoiceRequest>(jsonContent, new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                    PropertyNameCaseInsensitive = true
                });

                if (jsonData != null)
                {
                    InvoiceData = jsonData;
                    NormalizeInvoiceData();
                    DisplayTransDateTime = InvoiceData.TransDateTime;
                    DisplayTransAmount = InvoiceData.TransAmount;
                    ModelState.Clear(); // ensure Razor uses the JSON payload values

                    _logger.LogInformation("{ServiceName} - {ActionName} received redirect for OrderID: {OrderID}", ServiceName, actionName, InvoiceData.OrderID);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{ServiceName} - {ActionName} error parsing JSON request", ServiceName, actionName);
                ErrorMessage = "接收轉頁資料時發生錯誤";
            }
        }
        // 顯示表單讓用戶填寫
        return Page();
    }

    public async Task<IActionResult> OnPostToEndPointAsync()
    {
        NormalizeInvoiceData();
        // 處理表單提交
        // 保留發票資訊顯示
        DisplayTransDateTime = InvoiceData.TransDateTime;
        DisplayTransAmount = InvoiceData.TransAmount;
        var actionName = nameof(OnPostToEndPointAsync);
        _logger.LogInformation("{ServiceName} - {ActionName} received invoice submission payload {@InvoiceData}",
            ServiceName, actionName, InvoiceData);

        // 判斷是否為初始轉頁（三個選項都沒填寫）
        bool isInitialRedirect = string.IsNullOrEmpty(InvoiceData.Email) &&
                                 string.IsNullOrEmpty(InvoiceData.CarrierID) &&
                                 string.IsNullOrEmpty(InvoiceData.BuyerIdentifier) &&
                                 string.IsNullOrEmpty(InvoiceData.LoveCode);

        // 如果是初始轉頁，直接顯示表單讓用戶填寫
        if (isInitialRedirect)
        {
            return Page();
        }

        if (!ModelState.IsValid)
        {
            var invalidFields = ModelState
                .Where(entry => entry.Value?.Errors.Count > 0)
                .Select(entry =>
                {
                    var fieldName = string.IsNullOrEmpty(entry.Key) ? "表單" : entry.Key;
                    var messages = string.Join("、", entry.Value!.Errors.Select(e => e.ErrorMessage));
                    return $"{fieldName}：{messages}";
                });

            ErrorMessage = "資料驗證失敗，請檢查欄位輸入";
            _logger.LogWarning("{ServiceName} - {ActionName} validation failed: {ValidationErrors}", ServiceName, actionName, string.Join(" | ", invalidFields));
            return Page();
        }

        // 驗證 Email, CarrierID, BuyerIdentifier, LoveCode 僅能擇一填寫
        int filledCount = 0;
        if (!string.IsNullOrEmpty(InvoiceData.Email)) filledCount++;
        if (!string.IsNullOrEmpty(InvoiceData.CarrierID)) filledCount++;
        if (!string.IsNullOrEmpty(InvoiceData.BuyerIdentifier)) filledCount++;
        if (!string.IsNullOrEmpty(InvoiceData.LoveCode)) filledCount++;

        if (filledCount == 0)
        {
            ErrorMessage = "Email、手機條碼、購買者統編、捐贈碼僅能擇一填寫，請填寫任一項";
            return Page();
        }

        if (filledCount > 1)
        {
            ErrorMessage = "Email、手機條碼、購買者統編、捐贈碼僅能擇一填寫，請刪除多餘輸入";
            return Page();
        }

        if (string.IsNullOrWhiteSpace(_endpointApiOptions.InvoiceApi))
        {
            ErrorMessage = "未設定外部發票 API 的位址";
            _logger.LogError("{ServiceName} - {ActionName} Invoice API endpoint is not configured", ServiceName, actionName);
            return Page();
        }

        try
        {
            // 呼叫 TODO API（這裡使用規格中的範例 API）
            // http://192.168.110.72:22055/api/Intella/invoiceInfo
            // {
            //     "identifier": "12345678",
            //     "transDateTime": "2026/01/08 12:00:00",
            //     "transAmount": "30",
            //     "deviceID": "test",
            //     "locationID": "1",
            //     "carPlateNum": "ABC1235",
            //     "orderID": "260109_1",
            //     "email": "",
            //     "carrierID": "/ab12345",
            //     "buyerIdentifier": "",
            //     "taxType": "1",
            //     "loveCode": ""
            // }
            var httpClient = _httpClientFactory.CreateClient();

            if (_debugMode)
            {
                await PostPaymentNotifyForDebugAsync(httpClient, actionName);
            }
            var requestBody = JsonSerializer.Serialize(InvoiceData, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });
            _logger.LogInformation("{ServiceName} - {ActionName} sending API payload: {RequestBody}",
                ServiceName, actionName, requestBody);

            var content = new StringContent(requestBody, Encoding.UTF8, "application/json");

            var response = await httpClient.PostAsync(_endpointApiOptions.InvoiceApi, content);

            var responseContent = await response.Content.ReadAsStringAsync();
            _logger.LogInformation("{ServiceName} - {ActionName} received API response status {StatusCode} body: {ResponseBody}",
                ServiceName, actionName, response.StatusCode, responseContent);
            var apiResponse = JsonSerializer.Deserialize<InvoiceResponse>(responseContent, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            });

            if (!response.IsSuccessStatusCode)
            {
                ErrorMessage = $"送出失敗：{apiResponse?.msg ?? response.StatusCode.ToString()}";
                _logger.LogWarning("{ServiceName} - {ActionName} submission failed with HTTP {StatusCode} for OrderID: {OrderID}",
                    ServiceName, actionName, response.StatusCode, InvoiceData.OrderID);
                return Page();
            }

            if (apiResponse?.msgCode == "0000")
            {
                SuccessMessage = "發票資訊送出成功！";
                TempData[nameof(DisplayTransDateTime)] = DisplayTransDateTime;
                TempData[nameof(DisplayTransAmount)] = DisplayTransAmount;
                TempData[InvoiceDataTempKey] = JsonSerializer.Serialize(InvoiceData);
                _logger.LogInformation("{ServiceName} - {ActionName} submitted successfully for OrderID: {OrderID}",
                    ServiceName, actionName, InvoiceData.OrderID);
                return RedirectToPage();
            }
            else
            {
                ErrorMessage = $"送出失敗：{apiResponse?.msg ?? "未知錯誤"}";
                _logger.LogWarning("{ServiceName} - {ActionName} submission failed: {Message}", ServiceName, actionName, apiResponse?.msg);
            }
        }
        catch (Exception ex)
        {
#if DEBUG
            ErrorMessage = $"系統錯誤：{ex.Message}";
#else
            ErrorMessage = $"系統錯誤";
#endif
            _logger.LogError(ex, "{ServiceName} - {ActionName} error submitting invoice for OrderID: {OrderID}",
                ServiceName, actionName, InvoiceData.OrderID);
        }

        return Page();
    }

    private async Task PostPaymentNotifyForDebugAsync(HttpClient httpClient, string actionName)
    {
        if (string.IsNullOrWhiteSpace(_endpointApiOptions.PaymentNotifyApi))
        {
            _logger.LogWarning("{ServiceName} - {ActionName} DebugMode enabled but PaymentNotify API endpoint is not configured",
                ServiceName, actionName);
            return;
        }

        try
        {
            var licensePlate = string.IsNullOrWhiteSpace(InvoiceData.CarPlateNum) ? "9M1234" : InvoiceData.CarPlateNum;
            var parkingArea = string.IsNullOrWhiteSpace(InvoiceData.LocationID) ? "1" : InvoiceData.LocationID;
            var orderId = string.IsNullOrWhiteSpace(InvoiceData.OrderID)
                ? $"Order{DateTime.UtcNow:yyyyMMddHHmmss}"
                : InvoiceData.OrderID;

            var amount = 0m;
            var rawAmount = InvoiceData.TransAmount?.Trim();
            if (!string.IsNullOrWhiteSpace(rawAmount))
            {
                if (!decimal.TryParse(rawAmount, NumberStyles.Any, CultureInfo.InvariantCulture, out amount) &&
                    !decimal.TryParse(rawAmount, NumberStyles.Any, CultureInfo.CurrentCulture, out amount))
                {
                    _logger.LogWarning("{ServiceName} - {ActionName} unable to parse TransAmount {TransAmount}, using 0 for debug PaymentNotify",
                        ServiceName, actionName, InvoiceData.TransAmount);
                    amount = 0m;
                }
            }

            var paymentNotifyPayload = new
            {
                licensePlate,
                parkingArea,
                orderId,
                amount
            };
            var payloadJson = JsonSerializer.Serialize(paymentNotifyPayload);
            _logger.LogInformation("{ServiceName} - {ActionName} sending debug PaymentNotify payload: {Payload}",
                ServiceName, actionName, payloadJson);

            using var content = new StringContent(payloadJson, Encoding.UTF8, "application/json");
            var response = await httpClient.PostAsync(_endpointApiOptions.PaymentNotifyApi, content);
            var responseContent = await response.Content.ReadAsStringAsync();
            _logger.LogInformation("{ServiceName} - {ActionName} debug PaymentNotify response status {StatusCode} body: {ResponseBody}",
                ServiceName, actionName, response.StatusCode, responseContent);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "{ServiceName} - {ActionName} failed to POST debug PaymentNotify but continuing",
                ServiceName, actionName);
        }
    }

    private void NormalizeInvoiceData()
    {
        InvoiceData ??= new InvoiceRequest();
        // InvoiceData.Identifier = InvoiceData.Identifier ?? string.Empty;
        // InvoiceData.TransDateTime = InvoiceData.TransDateTime ?? string.Empty;
        // InvoiceData.TransAmount = InvoiceData.TransAmount ?? string.Empty;
        // InvoiceData.DeviceID = InvoiceData.DeviceID ?? string.Empty;
        // InvoiceData.LocationID = InvoiceData.LocationID ?? string.Empty;
        // InvoiceData.CarPlateNum = InvoiceData.CarPlateNum ?? string.Empty;
        // InvoiceData.OrderID = InvoiceData.OrderID ?? string.Empty;
        // InvoiceData.TaxType = InvoiceData.TaxType ?? string.Empty;
        //
        // InvoiceData.Email = InvoiceData.Email ?? string.Empty;
        // InvoiceData.CarrierID = InvoiceData.CarrierID ?? string.Empty;
        // InvoiceData.BuyerIdentifier = InvoiceData.BuyerIdentifier ?? string.Empty;
        // InvoiceData.LoveCode = InvoiceData.LoveCode ?? string.Empty;
    }

    private void RestoreDisplayValuesFromTempData()
    {
        if (TempData.TryGetValue(nameof(DisplayTransDateTime), out var transDateObj) &&
            transDateObj is string transDate)
        {
            DisplayTransDateTime = transDate;
        }

        if (TempData.TryGetValue(nameof(DisplayTransAmount), out var amountObj) &&
            amountObj is string amount)
        {
            DisplayTransAmount = amount;
        }
    }

    private void RestoreInvoiceDataFromTempData()
    {
        if (!TempData.TryGetValue(InvoiceDataTempKey, out var invoiceDataObj) ||
            invoiceDataObj is not string invoiceDataJson ||
            string.IsNullOrWhiteSpace(invoiceDataJson))
        {
            return;
        }

        try
        {
            var restoredData = JsonSerializer.Deserialize<InvoiceRequest>(invoiceDataJson, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            });

            if (restoredData != null)
            {
                InvoiceData = restoredData;
            }
        }
        catch (JsonException ex)
        {
            _logger.LogWarning(ex, "{ServiceName} - {ActionName} failed to restore invoice data from temp data",
                ServiceName, nameof(RestoreInvoiceDataFromTempData));
        }
    }
}
