using System;
using System.Collections.Generic;
using System.Data.Odbc;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using AS400API.Infrastructure;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;

namespace AS400API.Endpoints;

public static class ORDUAGEndpoint
{
    private static readonly string[] ColumnNames =
    [
        "AG_CODE_COMPANY",
        "AG_CODE_CURRENCY",
        "AG_CODE_NUMBER",
        "AGENT_AGENCY",
        "AGENT_HOME_NAAD",
        "AGENT_MAIL_NAAD",
        "AGENT_PHONE",
        "AGENT_STAT",
        "AGENT_START_DATE",
        "AGENT_DATE_TERM",
        "AGENT_FILLER_01",
        "AGENT_QUALITY_BONUS",
        "AGENT_MARKET_BONUS",
        "AGENT_NON_HOUSED_COMP",
        "AGENT_OTHER_COMP",
        "AGENT_FINANCE",
        "AGENT_FILLER_02",
        "AGENT_PAY_NOPAY",
        "AGENT_HOLD_BACK",
        "AGENT_DEVELOPMENT_AMT",
        "AGENT_NON_MED_PRIV",
        "AGENT_FRINGE_BENEFIT",
        "AGENT_FILLER_03",
        "AGENT_REGION",
        "AGENT_SPONSOR_LIC",
        "AGENT_PRIM_LIC_NO",
        "AGENT_QUALIFICATION",
        "AGENT_PREV_ID",
        "AGENT_FILLER_05",
        "AGENT_FYR_COMM_CURMTH",
        "AGENT_FYR_COMM_YTD",
        "AGENT_RENEWAL_COMM_CURMTH",
        "AGENT_RENEWAL_COMM_YTD",
        "AGENT_SERVFEE_CURMTH",
        "AGENT_SERVFEE_YTD",
        "AGENT_SINGPRM_CURMTH",
        "AGENT_SINGPRM_YTD",
        "AGENT_OVERRIDE_CURMTH",
        "AGENT_OVERRIDE_YTD",
        "AGENT_PAY_DED_CURMTH",
        "AGENT_PAY_DED_YTD",
        "AGENT_PAYABLE_BALANCE_YTD",
        "AGENT_NET_FYR_COMM1",
        "AGENT_NET_FYR_COMM2",
        "AGENT_NET_FYR_COMM3",
        "AGENT_NET_FYR_COMM4",
        "AGENT_NET_FYR_COMM5",
        "AGENT_NET_FYR_COMM6",
        "AGENT_NET_FYR_COMM7",
        "AGENT_NET_FYR_COMM8",
        "AGENT_NET_FYR_COMM9",
        "AGENT_NET_FYR_COMM10",
        "AGENT_NET_FYR_COMM11",
        "AGENT_NET_FYR_COMM12",
        "AGENT_ASGNMT_MADE_AMT",
        "AGENT_ASGNMT_REC_AMT",
        "AGENT_NET_FYR_LIFE_COMM1",
        "AGENT_NET_FYR_LIFE_COMM2",
        "AGENT_NET_FYR_LIFE_COMM3",
        "AGENT_NET_FYR_LIFE_COMM4",
        "AGENT_NET_FYR_LIFE_COMM5",
        "AGENT_NET_FYR_LIFE_COMM6",
        "AGENT_NET_FYR_LIFE_COMM7",
        "AGENT_NET_FYR_LIFE_COMM8",
        "AGENT_NET_FYR_LIFE_COMM9",
        "AGENT_NET_FYR_LIFE_COMM10",
        "AGENT_NET_FYR_LIFE_COMM11",
        "AGENT_NET_FYR_LIFE_COMM12",
        "AGENT_FYC_SINGPRM_CURMTH",
        "AGENT_FYC_SINGPRM_YTD",
        "AGENT_FYR_LIFE_COMM_CURMTH",
        "AGENT_FYR_LIFE_COMM_YTD",
        "AGENT_REN_LIFE_COMM_CURMTH",
        "AGENT_REN_LIFE_COMM_YTD",
        "AGENT_QUALITY_BON_CURMTH",
        "AGENT_QUALITY_BON_YTD",
        "AGENT_MARKET_BON_CURMTH",
        "AGENT_MARKET_BON_YTD",
        "AGENT_NON_HOUSED_CURMTH",
        "AGENT_NON_HOUSED_YTD",
        "AGENT_APP_CNT_LSTMTH",
        "AGENT_APP_CNT_YTM",
        "AGENT_TOT_PREM_WRIT_LSTMTH",
        "AGENT_TOT_PREM_WRIT_YTM",
        "AGENT_LIFE_PREM_WRIT_LSTMTH",
        "AGENT_LIFE_PREM_WRIT_YTM",
        "AGENT_TOT_FYP_CURMTH",
        "AGENT_TOT_FYP_YTD",
        "AGENT_TOT_REN_CURMTH",
        "AGENT_TOT_REN_YTD",
        "AGENT_PERSISTENCY1",
        "AGENT_PERSISTENCY2",
        "AGENT_PERSISTENCY3",
        "AGENT_PERSISTENCY4",
        "AGENT_PRODUCTION1",
        "AGENT_PRODUCTION2",
        "AGENT_PRODUCTION3",
        "AGENT_PRODUCTION4",
        "AGENT_MDRT1",
        "AGENT_MDRT2",
        "AGENT_MDRT3",
        "AGENT_MDRT4",
        "AGENT_CONVENTIONS1",
        "AGENT_CONVENTIONS2",
        "AGENT_CONVENTIONS3",
        "AGENT_CONVENTIONS4",
        "AGENT_APPTIVITY_AWARD1",
        "AGENT_APPTIVITY_AWARD2",
        "AGENT_APPTIVITY_AWARD3",
        "AGENT_APPTIVITY_AWARD4",
        "AGENT_NQA1",
        "AGENT_NQA2",
        "AGENT_NQA3",
        "AGENT_NQA4",
        "AG_BLACKLIST_RSN",
        "AG_TAX_NUMBER",
        "AG_DIST_CHANEL_CODE",
        "AGENT_LIAISON_OFF",
        "AGENT_FILLER_07",
        "AG_LIC_FPO",
        "AG_LIC_START_DATE",
        "AG_LIC_END_DATE",
        "AG_GUARANTOR_NO",
        "AG_GUARANTOR_SW",
        "AG_BANK_ACCOUNT",
        "AG_PREV_TAX_RATE",
        "AG_CURR_TAX_RATE",
        "AG_PRESERVE_AGENT",
        "AG_LIC_REQ_DATE",
        "AG_BLACKLIST_IND",
        "AGENT_FILLER_06"
    ];

    private static readonly Regex LibraryNamePattern = new("^[A-Z0-9_]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase);

    public static RouteGroupBuilder MapORDUAGEndpoints(this RouteGroupBuilder group)
    {
        group.MapGet("/v1/orduag/query", async (
                [AsParameters] OrduagQuery query,
                OdbcConnection conn,
                ILoggerFactory loggerFactory) =>
            {
                var logger = loggerFactory.CreateLogger("ORDUAGEndpoint");
                try
                {
                    await conn.OpenAsync();

                    var page = query.Page.GetValueOrDefault(1);
                    if (page < 1)
                    {
                        logger.LogWarning("Invalid page value {Page}; defaulting to 1", query.Page);
                        page = 1;
                    }

                    var pageSize = query.PageSize.GetValueOrDefault(50);
                    pageSize = Math.Clamp(pageSize, 1, 500);

                    var offsetLong = (long)(page - 1) * pageSize;
                    if (offsetLong > int.MaxValue)
                    {
                        return Results.BadRequest(new { error = "Requested page is too large." });
                    }

                    var offset = (int)offsetLong;

                    var sqlBuilder = new StringBuilder();
                    sqlBuilder.AppendLine("SELECT");
                    sqlBuilder.AppendLine(string.Join(",\n", ColumnNames.Select(column => $"    {column}")));
                    sqlBuilder.AppendLine("FROM CP3FPRD.ORDUAG");
                    sqlBuilder.AppendLine("WHERE 1 = 1");

                    var parameterValues = new List<object?>();

                    if (!string.IsNullOrWhiteSpace(query.CodeNumber))
                    {
                        sqlBuilder.AppendLine("  AND AG_CODE_NUMBER = ?");
                        parameterValues.Add(query.CodeNumber.Trim());
                    }

                    if (!string.IsNullOrWhiteSpace(query.AgentAgency))
                    {
                        sqlBuilder.AppendLine("  AND AGENT_AGENCY = ?");
                        parameterValues.Add(query.AgentAgency.Trim());
                    }

                    if (!string.IsNullOrWhiteSpace(query.AgentStat))
                    {
                        sqlBuilder.AppendLine("  AND AGENT_STAT = ?");
                        parameterValues.Add(query.AgentStat.Trim());
                    }

                    sqlBuilder.AppendLine("ORDER BY AG_CODE_NUMBER");
                    sqlBuilder.AppendLine("OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");

                    parameterValues.Add(offset);
                    parameterValues.Add(pageSize);

                    await using var command = conn.CreateCommand();
                    command.CommandText = sqlBuilder.ToString();

                    foreach (var value in parameterValues)
                    {
                        var parameter = command.CreateParameter();
                        parameter.Value = value;
                        command.Parameters.Add(parameter);
                    }

                    var rows = new List<IDictionary<string, object>>();
                    await using (var reader = await command.ExecuteReaderAsync())
                    {
                        while (await reader.ReadAsync())
                        {
                            var row = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
                            for (var i = 0; i < reader.FieldCount; i++)
                            {
                                var value = reader.GetNormalizedValue(i);
                                row[reader.GetName(i)] = value!;
                            }

                            rows.Add(row);
                        }
                    }

                    logger.LogInformation(
                        "Fetched {Count} ORDUAG rows with filters codeNumber={CodeNumber}, agency={Agency}, status={Status}, page={Page}, pageSize={PageSize}",
                        rows.Count,
                        query.CodeNumber,
                        query.AgentAgency,
                        query.AgentStat,
                        page,
                        pageSize);

                    var formatted = rows.Select(dict => dict.ToCamelCaseDictionary()).ToList();
                    return Results.Ok(formatted);
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, "Failed to list tables for AS/400 ORDUAG");
                    return Results.Problem($"Failed to list tables: {ex.Message}");
                }
            })
            // .RequireAuthorization(AuthPolicies.RequireOperator)
            .WithSummary("List tables for a specific AS/400 library (schema)");

        return group;
    }

    public sealed class OrduagQuery
    {
        public string? CodeNumber { get; init; }

        public string? AgentAgency { get; init; }

        public string? AgentStat { get; init; }

        public int? Page { get; init; }

        public int? PageSize { get; init; }
    }
}
