import axios from 'axios';
import logger from '../utils/logger';
import { Dealer } from '../models/Dealer';
import { ActivityType } from '../models/ActivityType';
import { WorkflowRequest } from '../models/WorkflowRequest';
import { ClaimInvoice } from '../models/ClaimInvoice';
import { InternalOrder } from '../models/InternalOrder';
import { User } from '../models/User';
import { DealerClaimDetails } from '../models/DealerClaimDetails';
import { DealerCompletionExpense } from '../models/DealerCompletionExpense';
import { DealerCompletionDetails } from '../models/DealerCompletionDetails';
import { ClaimInvoiceItem } from '../models/ClaimInvoiceItem';
import { findDealerLocally, DealerInfo } from './dealer.service';

/**
 * PWC E-Invoice Integration Service
 * Handles communication with PWC API for signed invoice generation
 */
const appDomain = process.env.APP_DOMAIN || 'royalenfield.com';

export class PWCIntegrationService {
    private apiUrl: string;
    private customerId: string;
    private token: string;

    constructor() {
        this.apiUrl = process.env.PWC_API_URL || 'https://api.qa.einvoice.aw.navigatetax.pwc.co.in/qa/v1/en/push';
        this.customerId = process.env.PWC_CUSTOMER_ID || '';
        this.token = process.env.PWC_TOKEN || '';
    }

    /**
     * Resolve GL Code based on Activity Type and Internal Order
     */
    private async resolveGLCode(activityTypeId: string, ioNumber?: string): Promise<string> {
        // Default Fallback or IO based logic if required
        // Based on "IO GL will be changed" comment in user screenshot
        if (ioNumber) {
            const io = await InternalOrder.findOne({ where: { ioNumber } });
            // Logic to derive GL from IO if needed
        }

        return '610000'; // Default placeholder
    }

    /**
     * Determine if HSN code belongs to a service (starts with 99)
     * @param hsnCode HSN/SAC code string
     * @param isServiceFlag Explicit flag from database if available
     */
    private isServiceHSN(hsnCode?: string, isServiceFlag?: boolean): "Y" | "N" {
        const cleanCode = String(hsnCode || '').trim();

        // Priority 1: SAC (Service Accounting Code) in India always starts with 99
        if (cleanCode.startsWith('99')) return "Y";

        // Priority 2: In Indian GST e-invoicing, any code not starting with 99 is treated as Goods (HSN)
        // Even if the user checked "Is Service", if the HSN is for goods, IsServc must be "N"
        // for valid NIC/IRP processing.
        return "N";
    }

    /**
     * Generate Signed Invoice via PWC API
     */
    async generateSignedInvoice(requestId: string, amount?: number, customInvoiceNumber?: string): Promise<{
        success: boolean;
        irn?: string;
        ackNo?: string;
        ackDate?: Date | string;
        signedInvoice?: string;
        qrCode?: string;
        qrImage?: string;
        rawResponse?: any;
        irpResponse?: any;
        error?: string;
        totalIgstAmt?: number;
        totalCgstAmt?: number;
        totalSgstAmt?: number;
        totalAssAmt?: number;
    }> {
        try {
            const request = await WorkflowRequest.findByPk(requestId, {
                include: [{ model: User, as: 'initiator' }, { model: DealerClaimDetails, as: 'claimDetails' }]
            });

            if (!request) return { success: false, error: 'Request not found' };

            const claimDetails = (request as any).claimDetails;
            const dealer: DealerInfo | null = await findDealerLocally(claimDetails?.dealerCode, claimDetails?.dealerEmail);
            const activity = await ActivityType.findOne({ where: { title: claimDetails?.activityType } });

            if (!dealer || !activity) {
                logger.warn(`[PWCIntegration] Dealer or Activity missing for request ${requestId}. Dealer lookup: ${claimDetails?.dealerCode} / ${claimDetails?.dealerEmail}`);
                return { success: false, error: 'Dealer or Activity details missing' };
            }

            // Fallback for amount if not provided
            const finalAmount = Number(amount || (request as any).amount || 0);

            // Fetch expenses early to be available for both Non-GST and GST logic
            const expenses = await DealerCompletionExpense.findAll({ where: { requestId } });

            // Helpers for precise formatting (return numbers as required by IRN schema)
            const formatAmount = (val: number) => Number(val.toFixed(2));
            const formatQty = (val: number) => Number(val.toFixed(3));
            const formatRate = (val: number) => Number(val.toFixed(2));

            // Extract State Code from Dealer GSTIN
            let dealerGst = dealer?.gstin;

            const isDevOrUat = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'uat';

            if (isDevOrUat) {
                logger.info(`[PWC] Running in ${process.env.NODE_ENV} mode. Original Dealer GST: ${dealerGst || 'empty'}`);
            }

            logger.info(`[PWC] Final GSTIN being used for authentication and seller: ${dealerGst}`);

            let dealerStateCode = "24"; // Default fallback (Gujarat for 24...)

            // Try to extract from GSTIN (first 2 chars)
            if (dealerGst && dealerGst.length >= 2 && !isNaN(Number(dealerGst.substring(0, 2)))) {
                dealerStateCode = dealerGst.substring(0, 2);
            } else if (dealer?.state) {
                // Approximate state code from state name or use 33 as default if it's RE state
                dealerStateCode = dealer.state.toLowerCase().includes('tamil') ? "33" : "24";
            }

            let itemList: any[] = [];
            let claimInvoiceItemsToCreate: any[] = [];
            let totalAssAmt = 0;
            let totalIgstAmt = 0;
            let totalCgstAmt = 0;
            let totalSgstAmt = 0;
            let totalInvVal = 0;

            const isIGST = dealerStateCode !== "33"; // If dealer state != Buyer state (33), it's IGST
            const isNonGSTActivity = activity.taxationType === 'Non GST';

            if (expenses && expenses.length > 0) {
                // Group expenses by HSN/SAC and GST Rate
                const groupedExpenses: Record<string, any> = {};

                expenses.forEach((expense: any) => {
                    const hsnCd = expense.hsnCode || "998311";
                    const gstRate = (expense.gstRate === undefined || expense.gstRate === null || Number(expense.gstRate) === 0)
                        ? 18
                        : Number(expense.gstRate);

                    const groupKey = `${hsnCd}_${gstRate}`;

                    const qty = Number(expense.quantity) || 1;
                    const amount = Number(expense.amount) || 0;
                    const baseAmt = amount * qty;

                    if (!groupedExpenses[groupKey]) {
                        groupedExpenses[groupKey] = {
                            hsnCd,
                            gstRate,
                            isService: this.isServiceHSN(hsnCd, expense.isService) === "Y",
                            baseAmt: 0,
                            igst: 0,
                            cgst: 0,
                            sgst: 0,
                            utgst: 0,
                            itemTotal: 0,
                            description: expense.description || activity.title,
                            expenseIds: [expense.expenseId],
                            hasUtgst: Number(expense.utgstRate || 0) > 0 || Number(expense.utgstAmt || 0) > 0
                        };
                    } else {
                        const nextDesc = expense.description || activity.title;
                        if (nextDesc && !groupedExpenses[groupKey].description.includes(nextDesc)) {
                            const updatedDesc = `${groupedExpenses[groupKey].description}, ${nextDesc}`;
                            // Truncate if too long for PWC (usually 100 chars)
                            groupedExpenses[groupKey].description = updatedDesc.length > 100
                                ? updatedDesc.substring(0, 97) + '...'
                                : updatedDesc;
                        }
                        groupedExpenses[groupKey].expenseIds.push(expense.expenseId);
                        if (Number(expense.utgstRate || 0) > 0 || Number(expense.utgstAmt || 0) > 0) {
                            groupedExpenses[groupKey].hasUtgst = true;
                        }
                    }

                    groupedExpenses[groupKey].baseAmt += baseAmt;
                });

                itemList = Object.values(groupedExpenses).map((group: any, index: number) => {
                    // STRICT CALCULATION: Recalculate tax based on grouped baseAmt to satisfy PWC validation (Tax = Base * Rate)
                    const groupGstRate = Number(group.gstRate || 0);
                    const groupBaseAmt = Number(group.baseAmt || 0);

                    let calcIgst = 0, calcCgst = 0, calcSgst = 0, calcUtgst = 0;
                    let calcIgstRate = 0, calcCgstRate = 0, calcSgstRate = 0, calcUtgstRate = 0;

                    if (isIGST) {
                        calcIgst = Number((groupBaseAmt * groupGstRate / 100).toFixed(2));
                        calcIgstRate = groupGstRate;
                    } else {
                        const halfRate = groupGstRate / 2;
                        const halfTax = Number((groupBaseAmt * halfRate / 100).toFixed(2));
                        calcCgst = halfTax;
                        calcCgstRate = halfRate;
                        // Use UTGST if detected in any expense of this group, otherwise SGST
                        if (group.hasUtgst) {
                            calcUtgst = halfTax;
                            calcUtgstRate = halfRate;
                        } else {
                            calcSgst = halfTax;
                            calcSgstRate = halfRate;
                        }
                    }

                    const calcTotalInvVal = Number((groupBaseAmt + calcIgst + calcCgst + calcSgst + calcUtgst).toFixed(2));

                    // Accumulate overall totals
                    totalAssAmt += groupBaseAmt;
                    totalIgstAmt += calcIgst;
                    totalCgstAmt += calcCgst;
                    totalSgstAmt += calcSgst + calcUtgst; // Sum of SGST and UTGST for summary
                    totalInvVal += calcTotalInvVal;

                    const slNo = index + 1;
                    const transactionCode = `${customInvoiceNumber}-${String(slNo).padStart(2, '0')}`;

                    claimInvoiceItemsToCreate.push({
                        requestId,
                        invoiceNumber: customInvoiceNumber,
                        transactionCode: transactionCode,
                        slNo: slNo,
                        description: group.description,
                        hsnCd: group.hsnCd,
                        qty: 1,
                        unit: "NOS",
                        unitPrice: formatAmount(groupBaseAmt),
                        assAmt: formatAmount(groupBaseAmt),
                        gstRt: formatRate(groupGstRate),
                        igstAmt: formatAmount(calcIgst),
                        cgstAmt: formatAmount(calcCgst),
                        sgstAmt: formatAmount(calcSgst),
                        utgstAmt: formatAmount(calcUtgst),
                        igstRate: formatRate(calcIgstRate),
                        cgstRate: formatRate(calcCgstRate),
                        sgstRate: formatRate(calcSgstRate),
                        utgstRate: formatRate(calcUtgstRate),
                        totItemVal: formatAmount(calcTotalInvVal),
                        isServc: group.isService ? "Y" : "N",
                        expenseIds: group.expenseIds
                    });

                    // PWC Payload Item format
                    const hsnForPwc = isNonGSTActivity ? group.hsnCd : "87141090"; // Force valid HSN for PWC Payload if GST
                    const isServcForPwc = isNonGSTActivity ? (group.isService ? "Y" : "N") : "N"; // 87141090 is Goods

                    return {
                        SlNo: String(slNo),
                        PrdNm: group.description,
                        PrdDesc: group.description,
                        HsnCd: hsnForPwc,
                        IsServc: isServcForPwc,
                        Qty: formatQty(1),
                        Unit: "NOS",
                        UnitPrice: formatAmount(groupBaseAmt),
                        TotAmt: formatAmount(groupBaseAmt),
                        Discount: 0,
                        PreTaxVal: formatAmount(groupBaseAmt),
                        AssAmt: formatAmount(groupBaseAmt),
                        GstRt: formatRate(groupGstRate),
                        IgstAmt: formatAmount(calcIgst),
                        CgstAmt: formatAmount(calcCgst),
                        SgstAmt: formatAmount(calcSgst + calcUtgst),
                        CesRt: 0,
                        CesAmt: 0,
                        CesNonAdValAmt: 0,
                        StateCesRt: 0,
                        StateCesAmt: 0,
                        StateCesNonAdValAmt: 0,
                        OthChrg: 0,
                        TotItemVal: formatAmount(calcTotalInvVal)
                    };
                });
            } else {
                // Fallback to single line item if no expenses found
                const gstRate = isNonGSTActivity ? 0 : 18;
                const assAmt = finalAmount;
                let igstAmt = 0, cgstAmt = 0, sgstAmt = 0;

                if (!isNonGSTActivity) {
                    igstAmt = isIGST ? (finalAmount * (gstRate / 100)) : 0;
                    cgstAmt = !isIGST ? (finalAmount * (gstRate / 200)) : 0;
                    sgstAmt = !isIGST ? (finalAmount * (gstRate / 200)) : 0;
                }
                const utgstAmt = 0; // Fallback assumes SGST for simplicity unless we detect state
                const totalTax = igstAmt + cgstAmt + sgstAmt + utgstAmt;
                const totalItemVal = finalAmount + totalTax;

                totalAssAmt = assAmt;
                totalIgstAmt = igstAmt;
                totalCgstAmt = cgstAmt;
                totalSgstAmt = sgstAmt;
                totalInvVal = totalItemVal;

                const slNo = 1;

                const fallbackHsn = "998311";
                const fallbackIsService = this.isServiceHSN(fallbackHsn) === "Y";
                const transactionCode = `${customInvoiceNumber}-${String(slNo).padStart(2, '0')}`;

                claimInvoiceItemsToCreate.push({
                    requestId,
                    invoiceNumber: customInvoiceNumber,
                    transactionCode: transactionCode,
                    slNo: slNo,
                    description: activity.title,
                    hsnCd: fallbackHsn,
                    qty: 1,
                    unit: "NOS",
                    unitPrice: formatAmount(assAmt),
                    assAmt: formatAmount(assAmt),
                    gstRt: formatRate(gstRate),
                    igstAmt: formatAmount(igstAmt),
                    cgstAmt: formatAmount(cgstAmt),
                    sgstAmt: formatAmount(sgstAmt),
                    utgstAmt: 0,
                    totItemVal: formatAmount(totalItemVal),
                    isServc: fallbackIsService ? "Y" : "N",
                    expenseIds: []
                });

                // PWC Payload Item format
                const hsnForPwc = isNonGSTActivity ? fallbackHsn : "87141090"; // Force valid HSN for PWC Payload if GST
                const isServcForPwc = isNonGSTActivity ? (fallbackIsService ? "Y" : "N") : "N"; // 87141090 is Goods

                itemList = [{
                    SlNo: String(slNo),
                    PrdNm: activity.title,
                    PrdDesc: activity.title,
                    HsnCd: hsnForPwc,
                    IsServc: isServcForPwc,
                    Qty: formatQty(1),
                    Unit: "NOS",
                    UnitPrice: formatAmount(assAmt),
                    TotAmt: formatAmount(assAmt),
                    Discount: 0,
                    PreTaxVal: formatAmount(assAmt),
                    AssAmt: formatAmount(assAmt),
                    GstRt: formatRate(gstRate),
                    IgstAmt: formatAmount(igstAmt),
                    CgstAmt: formatAmount(cgstAmt),
                    SgstAmt: formatAmount(sgstAmt),
                    CesRt: 0,
                    CesAmt: 0,
                    CesNonAdValAmt: 0,
                    StateCesRt: 0,
                    StateCesAmt: 0,
                    StateCesNonAdValAmt: 0,
                    OthChrg: 0,
                    TotItemVal: formatAmount(totalItemVal)
                }];
            }

            // NEW LOGIC: Check for Non-GST Activity
            if (isNonGSTActivity) {
                logger.info(`[PWC] Activity ${activity.title} is Non-GST. Skipping IRN generation.`);

                // Persistence for Non-GST (Awaited)
                await ClaimInvoiceItem.destroy({ where: { requestId } });
                if (claimInvoiceItemsToCreate.length > 0) {
                    await ClaimInvoiceItem.bulkCreate(claimInvoiceItemsToCreate);
                    logger.info(`[PWCIntegration] Persisted ${claimInvoiceItemsToCreate.length} line items for Non-GST request ${requestId}`);
                }

                // Return internal success response (No IRN)
                return {
                    success: true,
                    irn: undefined,
                    ackNo: undefined,
                    ackDate: new Date(),
                    signedInvoice: undefined,
                    qrCode: undefined, // No QR for Non-GST
                    qrImage: undefined,
                    totalIgstAmt: formatAmount(totalIgstAmt),
                    totalCgstAmt: formatAmount(totalCgstAmt),
                    totalSgstAmt: formatAmount(totalSgstAmt),
                    totalAssAmt: formatAmount(totalAssAmt)
                };
            }

            // Construct PWC Payload - Aligned with sample format provided by user EXACTLY

            const payload = [
                {
                    User_GSTIN: dealerGst, // Portal User ID (Dealer's own GST)
                    Version: "1.01",
                    IRN: "",
                    SourceSystem: "RE_WORKFLOW",
                    is_irn: "Y",
                    is_ewb: "N",
                    email: isDevOrUat ? "jayesh.jacob1@pwc.com" : ((request as any).initiator?.email || `system@${appDomain}`),
                    TranDtls: {
                        TaxSch: "GST",
                        OutwardInward: "",
                        SubType: "SUPPLY",
                        SubTypeDescription: "Others",
                        SupTyp: "B2B",
                        RegRev: "N",
                        Typ: "REG",
                        EcmGstin: "",
                        IgstOnIntra: "",
                        DiffPercentage: "0",
                        Taxability: (totalIgstAmt + totalCgstAmt + totalSgstAmt) > 0 ? "Taxable" : "Exempted",
                        InterIntra: "",
                        CancelFlag: "N",
                        CnlRsn: "1",
                        CnlRem: "d"
                    },
                    DocDtls: {
                        Typ: "Inv",
                        No: customInvoiceNumber || (request as any).requestNumber || `INV-${Date.now()}`,
                        Dt: new Date().toLocaleDateString('en-GB') // DD/MM/YYYY
                    },
                    SellerDtls: {
                        Gstin: dealerGst, // Actual dealer GST from local table
                        LglNm: dealer?.dealerName || 'Dealer',
                        TrdNm: dealer?.dealerName || 'Dealer',
                        Addr1: dealer?.city || "Address Line 1",
                        Loc: dealer?.city || "Location",
                        Pin: Number(dealer?.pincode || 600001),
                        Stcd: dealerStateCode,
                        Ph: dealer?.phone || "9998887776",
                        Em: dealer?.email || "Supplier@inv.com"
                    },
                    BuyerDtls: {
                        Gstin: "33AAACE3882D1ZZ", // Royal Enfield GST (Tamil Nadu)
                        LglNm: "ROYAL ENFIELD (A UNIT OF EICHER MOTORS LTD)",
                        TrdNm: "ROYAL ENFIELD",
                        Addr1: "No. 2, Thiruvottiyur High Road",
                        Loc: "Thiruvottiyur",
                        Pin: 600019,
                        Stcd: "33",
                        Pos: "33"
                    },
                    ItemList: itemList,
                    ValDtls: {
                        AssVal: formatAmount(totalAssAmt),
                        IgstVal: formatAmount(totalIgstAmt),
                        CgstVal: formatAmount(totalCgstAmt),
                        SgstVal: formatAmount(totalSgstAmt), // Sum of SGST and UTGST for summary
                        TotInvVal: formatAmount(totalInvVal)
                    }
                }
            ];

            logger.info(`[PWC] Sending e-invoice request for ${request.requestNumber}`);
            logger.info(`[PWC] Payload for ${request.requestNumber}: ${JSON.stringify(payload)}`);

            const response = await axios.post(this.apiUrl, payload, {
                headers: {
                    'customerid': this.customerId,
                    'token': this.token
                }
            });

            // Parse PWC Response based on provided structure
            // Sample response is an array: [{ pwc_response, irp_response, qr_b64_encoded }]
            const responseData = Array.isArray(response.data) ? response.data[0] : response.data;
            const irpData = responseData?.irp_response?.data;
            const nicData = irpData?.nic_response_data;
            const qrB64 = responseData?.qr_b64_encoded;

            let irn = nicData?.Irn;
            let ackNo = nicData?.AckNo;
            let ackDate = nicData?.AckDt;
            let signedInvoice = nicData?.SignedInvoice;
            let qrCode = nicData?.SignedQRCode;

            // Handle Duplicate IRN Case
            if (!irn && irpData?.InfoDtls) {
                const dupInfo = irpData.InfoDtls.find((info: any) => info.InfCd === 'DUPIRN');
                if (dupInfo?.Desc) {
                    irn = dupInfo.Desc.Irn;
                    ackNo = dupInfo.Desc.AckNo;
                    ackDate = dupInfo.Desc.AckDt;
                    logger.info(`[PWC] Handled duplicate IRN for ${request.requestNumber}: ${irn}`);
                }
            }

            if (!irn) {
                const mainMsg = responseData?.pwc_response?.message || responseData?.irp_response?.message || 'E-Invoice generation failed';

                // Extract detailed error messages from irp_response.data.error_details
                const errorDetails = responseData?.irp_response?.data?.error_details;
                let errorMessage = mainMsg;

                if (Array.isArray(errorDetails) && errorDetails.length > 0) {
                    const detailsStr = errorDetails.map((e: any) => `${e.ErrorCode}: ${e.ErrorMessage}`).join('; ');
                    errorMessage = `${mainMsg} - [${detailsStr}]`;
                }

                // Also check validation remarks
                const validationRemarks = responseData?.pwc_response?.validation_remarks;
                if (validationRemarks && Object.keys(validationRemarks).length > 0) {
                    errorMessage += ` - Validation: ${JSON.stringify(validationRemarks)}`;
                }

                logger.error(`[PWC] E-Invoice failed for ${request.requestNumber}: ${errorMessage}`);
                return { success: false, error: errorMessage };
            }

            // Persistence for GST (Awaited) - Only if IRN generation was successful
            await ClaimInvoiceItem.destroy({ where: { requestId } });
            if (claimInvoiceItemsToCreate.length > 0) {
                await ClaimInvoiceItem.bulkCreate(claimInvoiceItemsToCreate);
                logger.info(`[PWCIntegration] Persisted ${claimInvoiceItemsToCreate.length} line items for GST request ${requestId}`);
            }

            // Update invoice with dealer GSTIN if available
            if (dealer?.gstin) {
                await ClaimInvoice.update({ consignorGsin: dealer.gstin }, { where: { requestId } })
                    .catch(err => logger.error(`[PWCIntegration] Error updating invoice consignorGsin:`, err));
            }

            return {
                success: true,
                irn,
                ackNo: String(ackNo),
                ackDate: ackDate ? new Date(ackDate) : undefined,
                signedInvoice,
                qrCode,
                qrImage: qrB64,
                rawResponse: responseData?.pwc_response,
                irpResponse: responseData?.irp_response,
                totalIgstAmt: Number(totalIgstAmt.toFixed(2)),
                totalCgstAmt: Number(totalCgstAmt.toFixed(2)),
                totalSgstAmt: Number(totalSgstAmt.toFixed(2)),
                totalAssAmt: Number(totalAssAmt.toFixed(2))
            };

        } catch (error) {
            logger.error('[PWC] Error generating e-invoice:', error);
            return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
        }
    }
}

export const pwcIntegrationService = new PWCIntegrationService();
