import { WorkflowRequest } from '@models/WorkflowRequest';
import { DealerClaimDetails } from '@models/DealerClaimDetails';
import { ClaimCreditNote } from '@models/ClaimCreditNote';
import { DealerProposalDetails } from '@models/DealerProposalDetails';
import { ClaimBudgetTracking } from '@models/ClaimBudgetTracking';
import { Op, QueryTypes } from 'sequelize';
import { sequelize } from '@config/database';
import dayjs from 'dayjs';
import logger from '@utils/logger';
import { User } from '@models/User';

interface DateRangeFilter {
  start: Date;
  end: Date;
}

interface DashboardKPIs {
  totalClaims: number;
  totalValue: number;
  approved: number;
  rejected: number;
  pending: number;
  credited: number;
  pendingCredit: number;
  approvedValue: number;
  rejectedValue: number;
  pendingValue: number;
  creditedValue: number;
  pendingCreditValue: number;
}

interface CategoryData {
  activityType: string;
  raised: number;
  raisedValue: number;
  approved: number;
  approvedValue: number;
  rejected: number;
  rejectedValue: number;
  pending: number;
  pendingValue: number;
  credited: number;
  creditedValue: number;
  pendingCredit: number;
  pendingCreditValue: number;
  approvalRate: number;
  creditRate: number;
}

export class DealerDashboardService {
  /**
   * Parse date range string to Date objects
   */
  private parseDateRange(dateRange?: string, startDate?: string, endDate?: string): DateRangeFilter {
    if (dateRange === 'custom' && startDate && endDate) {
      const start = dayjs(startDate).startOf('day').toDate();
      const end = dayjs(endDate).endOf('day').toDate();
      const now = dayjs();
      const actualEnd = end > now.toDate() ? now.endOf('day').toDate() : end;
      return { start, end: actualEnd };
    }
    
    if (dateRange === 'custom' && (!startDate || !endDate)) {
      const now = dayjs();
      return {
        start: now.subtract(30, 'day').startOf('day').toDate(),
        end: now.endOf('day').toDate()
      };
    }
    
    const now = dayjs();
    
    switch (dateRange) {
      case 'today':
        return {
          start: now.startOf('day').toDate(),
          end: now.endOf('day').toDate()
        };
      case 'week':
        return {
          start: now.startOf('week').toDate(),
          end: now.endOf('week').toDate()
        };
      case 'month':
        return {
          start: now.startOf('month').toDate(),
          end: now.endOf('month').toDate()
        };
      case 'quarter':
        const currentMonth = now.month();
        const quarterStartMonth = Math.floor(currentMonth / 3) * 3;
        return {
          start: now.month(quarterStartMonth).startOf('month').toDate(),
          end: now.month(quarterStartMonth + 2).endOf('month').toDate()
        };
      case 'year':
        return {
          start: now.startOf('year').toDate(),
          end: now.endOf('year').toDate()
        };
      default:
        return {
          start: now.subtract(30, 'day').startOf('day').toDate(),
          end: now.endOf('day').toDate()
        };
    }
  }

  /**
   * Get dealer email from user email or user ID
   */
  private async getDealerEmail(userEmail?: string, userId?: string): Promise<string | null> {
    try {
      if (userEmail) {
        // Check if user email matches a dealer email in dealer_claim_details
        const dealerClaim = await DealerClaimDetails.findOne({
          where: {
            dealerEmail: { [Op.iLike]: userEmail.toLowerCase() }
          },
          limit: 1
        });
        if (dealerClaim) {
          return dealerClaim.dealerEmail?.toLowerCase() || null;
        }
      }

      if (userId) {
        // Get user email from userId
        const user = await User.findByPk(userId);
        if (user?.email) {
          const dealerClaim = await DealerClaimDetails.findOne({
            where: {
              dealerEmail: { [Op.iLike]: user.email.toLowerCase() }
            },
            limit: 1
          });
          if (dealerClaim) {
            return dealerClaim.dealerEmail?.toLowerCase() || null;
          }
        }
      }

      return null;
    } catch (error) {
      logger.error('[DealerDashboard] Error getting dealer email:', error);
      return null;
    }
  }

  /**
   * Get dashboard KPIs for dealer
   */
  async getDashboardKPIs(
    userEmail?: string,
    userId?: string,
    dateRange?: string,
    startDate?: string,
    endDate?: string
  ): Promise<{ kpis: DashboardKPIs; categoryData: CategoryData[] }> {
    try {
      const dealerEmail = await this.getDealerEmail(userEmail, userId);
      if (!dealerEmail) {
        logger.warn('[DealerDashboard] No dealer email found for user');
        return {
          kpis: {
            totalClaims: 0,
            totalValue: 0,
            approved: 0,
            rejected: 0,
            pending: 0,
            credited: 0,
            pendingCredit: 0,
            approvedValue: 0,
            rejectedValue: 0,
            pendingValue: 0,
            creditedValue: 0,
            pendingCreditValue: 0,
          },
          categoryData: []
        };
      }

      const applyDateRange = dateRange !== undefined && dateRange !== null && dateRange !== 'all';
      const range = applyDateRange ? this.parseDateRange(dateRange, startDate, endDate) : null;

      // Build date filter
      const dateFilter = applyDateRange && range
        ? `AND (
            (wf.submission_date BETWEEN :start AND :end AND wf.submission_date IS NOT NULL)
            OR (wf.submission_date IS NULL AND wf.created_at BETWEEN :start AND :end)
          )`
        : `1=1`;

      const replacements: any = { dealerEmail: dealerEmail.toLowerCase() };
      if (applyDateRange && range) {
        replacements.start = range.start;
        replacements.end = range.end;
      }

      // Get all dealer claims with their details
      // Filter by both workflow_type and template_type for compatibility
      const claimsQuery = `
        SELECT
          wf.request_id,
          wf.status,
          dcd.activity_type,
          COALESCE(dpd.total_estimated_budget, cbt.proposal_estimated_budget, 0)::numeric AS estimated_budget,
          COALESCE(cbt.approved_budget, cbt.proposal_estimated_budget, dpd.total_estimated_budget, 0)::numeric AS approved_budget,
          cbt.final_claim_amount::numeric AS final_claim_amount,
          ccn.credit_note_number,
          ccn.credit_note_date,
          ccn.credit_amount::numeric AS credit_note_amount
        FROM workflow_requests wf
        INNER JOIN dealer_claim_details dcd ON wf.request_id = dcd.request_id
        LEFT JOIN dealer_proposal_details dpd ON wf.request_id = dpd.request_id
        LEFT JOIN claim_budget_tracking cbt ON wf.request_id = cbt.request_id
        LEFT JOIN claim_credit_notes ccn ON wf.request_id = ccn.request_id
        WHERE (wf.workflow_type = 'CLAIM_MANAGEMENT' OR wf.template_type = 'DEALER CLAIM')
          AND wf.is_draft = false
          AND (wf.is_deleted IS NULL OR wf.is_deleted = false)
          AND dcd.dealer_email ILIKE :dealerEmail
          AND ${dateFilter}
      `;

      const claims = await sequelize.query(claimsQuery, {
        replacements,
        type: QueryTypes.SELECT
      }) as any[];

      // Calculate KPIs
      const kpis: DashboardKPIs = {
        totalClaims: claims.length,
        totalValue: 0,
        approved: 0,
        rejected: 0,
        pending: 0,
        credited: 0,
        pendingCredit: 0,
        approvedValue: 0,
        rejectedValue: 0,
        pendingValue: 0,
        creditedValue: 0,
        pendingCreditValue: 0,
      };

      // Group by category
      const categoryMap = new Map<string, CategoryData>();

      for (const claim of claims) {
        const activityType = claim.activity_type || 'Unknown';
        const status = (claim.status || '').toUpperCase();
        const estimatedBudget = parseFloat(claim.estimated_budget || 0);
        const approvedBudget = parseFloat(claim.approved_budget || estimatedBudget);
        const finalClaimAmount = parseFloat(claim.final_claim_amount || approvedBudget);
        const hasCreditNote = !!(claim.credit_note_number && claim.credit_note_date);
        const creditNoteAmount = parseFloat(claim.credit_note_amount || finalClaimAmount);

        // Initialize category if not exists
        if (!categoryMap.has(activityType)) {
          categoryMap.set(activityType, {
            activityType,
            raised: 0,
            raisedValue: 0,
            approved: 0,
            approvedValue: 0,
            rejected: 0,
            rejectedValue: 0,
            pending: 0,
            pendingValue: 0,
            credited: 0,
            creditedValue: 0,
            pendingCredit: 0,
            pendingCreditValue: 0,
            approvalRate: 0,
            creditRate: 0,
          });
        }

        const category = categoryMap.get(activityType)!;

        // Count and values by status
        category.raised++;
        category.raisedValue += estimatedBudget;
        kpis.totalValue += estimatedBudget;

        if (status === 'APPROVED' || status === 'CLOSED') {
          category.approved++;
          category.approvedValue += approvedBudget;
          kpis.approved++;
          kpis.approvedValue += approvedBudget;

          if (hasCreditNote) {
            category.credited++;
            category.creditedValue += creditNoteAmount;
            kpis.credited++;
            kpis.creditedValue += creditNoteAmount;
          } else {
            category.pendingCredit++;
            category.pendingCreditValue += finalClaimAmount;
            kpis.pendingCredit++;
            kpis.pendingCreditValue += finalClaimAmount;
          }
        } else if (status === 'REJECTED') {
          category.rejected++;
          category.rejectedValue += estimatedBudget;
          kpis.rejected++;
          kpis.rejectedValue += estimatedBudget;
        } else if (status === 'PENDING' || status === 'IN_PROGRESS') {
          category.pending++;
          category.pendingValue += estimatedBudget;
          kpis.pending++;
          kpis.pendingValue += estimatedBudget;
        }
      }

      // Calculate rates for each category
      const categoryData = Array.from(categoryMap.values()).map(cat => {
        cat.approvalRate = cat.raised > 0 ? (cat.approved / cat.raised) * 100 : 0;
        cat.creditRate = cat.approved > 0 ? (cat.credited / cat.approved) * 100 : 0;
        return cat;
      });

      return {
        kpis,
        categoryData
      };
    } catch (error) {
      logger.error('[DealerDashboard] Error fetching dashboard KPIs:', error);
      throw error;
    }
  }
}

export const dealerDashboardService = new DealerDashboardService();

