/**
 * Dealer Service
 * Handles dealer-related operations for claim management
 * Fetches from dealers table and checks if dealer is logged in (domain_id exists in users table)
 */

import { User } from '../models/User';
import { Dealer } from '../models/Dealer';
import { Op } from 'sequelize';
import logger from '../utils/logger';

export interface DealerInfo {
  dealerId: string;
  userId?: string | null; // User ID if dealer is logged in
  email: string; // domain_id from dealers table
  dealerCode: string; // dlrcode from dealers table
  dealerName: string; // dealership from dealers table
  displayName: string; // dealer_principal_name from dealers table
  phone?: string; // dp_contact_number from dealers table
  department?: string;
  designation?: string;
  isLoggedIn: boolean; // Whether dealer's domain_id exists in users table
  salesCode?: string | null;
  serviceCode?: string | null;
  gearCode?: string | null;
  gmaCode?: string | null;
  region?: string | null;
  state?: string | null;
  district?: string | null;
  city?: string | null;
  dealerPrincipalName?: string | null;
  dealerPrincipalEmailId?: string | null;
  gstin?: string | null;
  pincode?: string | null;
  itemGroup?: string | null;
  parentCode?: string | null;
}

/**
 * Get all dealers from dealers table
 * Checks if dealer is logged in by matching domain_id with users.email
 * @param searchTerm - Optional search term to filter dealers
 * @param limit - Maximum number of records to return (default: 10)
 */
export async function getAllDealers(searchTerm?: string, limit: number = 10): Promise<DealerInfo[]> {
  try {
    // Build where clause for search
    const whereClause: any = {
      isActive: true,
    };

    if (searchTerm && searchTerm.trim()) {
      whereClause[Op.or] = [
        { dealership: { [Op.iLike]: `%${searchTerm}%` } as any },
        { dealerPrincipalName: { [Op.iLike]: `%${searchTerm}%` } as any },
        { domainId: { [Op.iLike]: `%${searchTerm}%` } as any },
        { dlrcode: { [Op.iLike]: `%${searchTerm}%` } as any },
        { salesCode: { [Op.iLike]: `%${searchTerm}%` } as any },
        { gearCode: { [Op.iLike]: `%${searchTerm}%` } as any },
      ];
    }

    const dealers = await Dealer.findAll({
      where: whereClause,
      order: [['dealership', 'ASC']],
      limit: limit, // Always limit results to specified limit (default 10)
    });

    // Get all domain_ids to check which dealers are logged in
    const domainIds = dealers
      .map((d) => d.domainId)
      .filter((id): id is string => id !== null && id !== undefined);

    // Check which domain_ids exist in users table
    const loggedInUsers = await User.findAll({
      where: {
        email: { [Op.in]: domainIds } as any,
        isActive: true,
      },
      attributes: ['userId', 'email', 'displayName', 'phone', 'department', 'designation'],
    });

    // Create a map of email -> user for quick lookup
    const userMap = new Map(loggedInUsers.map((u) => [u.email.toLowerCase(), u]));

    // Map dealers to DealerInfo with login status
    return dealers.map((dealer) => {
      const domainId = dealer.domainId?.toLowerCase() || '';
      const user = domainId ? userMap.get(domainId) : null;
      const isLoggedIn = !!user;

      return {
        dealerId: dealer.dealerId,
        userId: user?.userId || null,
        email: dealer.domainId || '',
        dealerCode: dealer.dlrcode || '',
        dealerName: dealer.dealership || dealer.dealerPrincipalName || '',
        displayName: dealer.dealerPrincipalName || dealer.dealership || '',
        phone: dealer.dpContactNumber || undefined,
        department: user?.department || undefined,
        designation: user?.designation || undefined,
        isLoggedIn,
        salesCode: dealer.salesCode || null,
        serviceCode: dealer.serviceCode || null,
        gearCode: dealer.gearCode || null,
        gmaCode: dealer.gmaCode || null,
        region: dealer.region || null,
        state: dealer.state || null,
        district: dealer.district || null,
        city: dealer.city || null,
        dealerPrincipalName: dealer.dealerPrincipalName || null,
        dealerPrincipalEmailId: dealer.dealerPrincipalEmailId || null,
        gstin: dealer.gst || null,
        pincode: dealer.showroomPincode || null,
        itemGroup: null, // Local dealer table doesn't have item group yet
        parentCode: null,
      };
    });
  } catch (error) {
    logger.error('[DealerService] Error fetching dealers:', error);
    throw error;
  }
}

import { dealerExternalService } from './dealerExternal.service';

/**
 * Get dealer by code (dlrcode / employeeId)
 * Validates dealer exists in User table with jobTitle 'Dealer' and matching employeeNumber.
 * Enriches with real-time data from External Dealer API.
 */
export async function getDealerByCode(dealerCode: string): Promise<DealerInfo | null> {
  try {
    // 1. Fetch data from External Dealer API (Source of truth for GSTIN, Address, etc.)
    let externalData = null;
    try {
      externalData = await dealerExternalService.getDealerByCode(dealerCode);
    } catch (err) {
      logger.warn(`[DealerService] External API failed for ${dealerCode}, falling back to database only.`);
    }

    // 2. Try to find in User table directly (Validation logic)
    // One dealer can have multiple codes but they share one parentCode.
    // If externalData has a parentcode, we check if there's a user record for that parentcode.
    const userLookupCode = externalData?.parentcode || dealerCode;

    const user = await User.findOne({
      where: {
        employeeNumber: userLookupCode,
        jobTitle: 'Dealer',
        isActive: true
      },
      attributes: ['userId', 'employeeNumber', 'email', 'displayName', 'phone', 'department', 'designation', 'jobTitle', 'mobilePhone']
    });

    if (user) {
      logger.info(`[DealerService] Dealer found in User table: ${dealerCode}`);
      logger.info(`[DealerService] Dealer mapping found via code ${userLookupCode}: ${dealerCode}`);
      return {
        dealerId: user.userId,
        userId: user.userId,
        email: user.email,
        dealerCode: dealerCode, // Return the originally requested code
        dealerName: externalData?.['dealer name'] || user.displayName || '',
        displayName: user.displayName || '',
        phone: externalData?.['dealer phone'] || (user as any).mobilePhone || user.phone || undefined,
        department: user.department || undefined,
        designation: user.designation || user.jobTitle || undefined,
        isLoggedIn: true,
        gstin: externalData?.gstin || null,
        city: externalData?.['re city'] || null,
        state: externalData?.['re state code'] || null,
        pincode: externalData?.pincode || null,
        itemGroup: externalData?.['item group'] || null,
        parentCode: externalData?.parentcode || null,
      };
    }

    // 3. Fallback to local Dealer table
    const dealer = await Dealer.findOne({
      where: {
        dlrcode: dealerCode,
        isActive: true,
      },
    });

    if (!dealer) {
      if (externalData) {
        logger.info(`[DealerService] Dealer found ONLY in External API: ${dealerCode}`);
        return {
          dealerId: dealerCode,
          userId: null,
          email: externalData['dealer email'] || '',
          dealerCode: dealerCode,
          dealerName: externalData['dealer name'],
          displayName: externalData['dealer name'],
          phone: externalData['dealer phone'] || undefined,
          isLoggedIn: false,
          gstin: externalData.gstin,
          city: externalData['re city'],
          state: externalData['re state code'],
          pincode: externalData.pincode,
          itemGroup: externalData['item group'] || null,
          parentCode: externalData.parentcode || null,
        };
      }
      logger.warn(`[DealerService] Dealer not found in any source: ${dealerCode}`);
      return null;
    }

    logger.info(`[DealerService] Dealer found in local Dealer table: ${dealerCode}`);
    return {
      dealerId: dealer.dealerId,
      userId: null,
      email: dealer.domainId || externalData?.['dealer email'] || '',
      dealerCode: dealer.dlrcode || dealerCode,
      dealerName: externalData?.['dealer name'] || dealer.dealership || dealer.dealerPrincipalName || '',
      displayName: dealer.dealerPrincipalName || dealer.dealership || '',
      phone: externalData?.['dealer phone'] || dealer.dpContactNumber || undefined,
      isLoggedIn: false,
      salesCode: dealer.salesCode || null,
      serviceCode: dealer.serviceCode || null,
      gearCode: dealer.gearCode || null,
      gmaCode: dealer.gmaCode || null,
      region: dealer.region || null,
      state: externalData?.['re state code'] || dealer.state || null,
      district: dealer.district || null,
      city: externalData?.['re city'] || dealer.city || null,
      dealerPrincipalName: dealer.dealerPrincipalName || null,
      dealerPrincipalEmailId: dealer.dealerPrincipalEmailId || null,
      gstin: externalData?.gstin || dealer.gst || null,
      pincode: externalData?.pincode || dealer.showroomPincode || null,
      itemGroup: externalData?.['item group'] || null,
      parentCode: externalData?.parentcode || null,
    };
  } catch (error) {
    logger.error('[DealerService] Error fetching dealer by code:', error);
    throw error;
  }
}

/**
 * Get dealer by email (domain_id)
 * Checks if dealer is logged in by matching domain_id with users.email
 */
export async function getDealerByEmail(email: string): Promise<DealerInfo | null> {
  try {
    const dealer = await Dealer.findOne({
      where: {
        domainId: { [Op.iLike]: email.toLowerCase() } as any,
        isActive: true,
      },
    });

    if (!dealer) {
      return null;
    }

    // Check if dealer is logged in (domain_id exists in users table)
    let user = null;
    if (dealer.domainId) {
      user = await User.findOne({
        where: {
          email: dealer.domainId.toLowerCase(),
          isActive: true,
        },
        attributes: ['userId', 'email', 'displayName', 'phone', 'department', 'designation'],
      });
    }

    const isLoggedIn = !!user;

    return {
      dealerId: dealer.dealerId,
      userId: user?.userId || null,
      email: dealer.domainId || '',
      dealerCode: dealer.dlrcode || '',
      dealerName: dealer.dealership || dealer.dealerPrincipalName || '',
      displayName: dealer.dealerPrincipalName || dealer.dealership || '',
      phone: dealer.dpContactNumber || user?.phone || undefined,
      department: user?.department || undefined,
      designation: user?.designation || undefined,
      isLoggedIn,
      salesCode: dealer.salesCode || null,
      serviceCode: dealer.serviceCode || null,
      gearCode: dealer.gearCode || null,
      gmaCode: dealer.gmaCode || null,
      region: dealer.region || null,
      state: dealer.state || null,
      district: dealer.district || null,
      city: dealer.city || null,
      dealerPrincipalName: dealer.dealerPrincipalName || null,
      dealerPrincipalEmailId: dealer.dealerPrincipalEmailId || null,
      gstin: dealer.gst || null,
      pincode: dealer.showroomPincode || null,
      itemGroup: null,
      parentCode: null,
    };
  } catch (error) {
    logger.error('[DealerService] Error fetching dealer by email:', error);
    throw error;
  }
}

/**
 * Search dealers by name, code, or email
 * Uses getAllDealers with search term
 * @param searchTerm - Search term to filter dealers
 * @param limit - Maximum number of records to return (default: 10)
 */
export async function searchDealers(searchTerm: string, limit: number = 10): Promise<DealerInfo[]> {
  try {
    return await getAllDealers(searchTerm, limit);
  } catch (error) {
    logger.error('[DealerService] Error searching dealers:', error);
    throw error;
  }
}

/**
 * Find dealer in local table by code or email (tiered lookup)
 * Used as a fallback until external API is available
 * @param dealerCode - Optional dealer code (dlrcode)
 * @param email - Optional email (domainId or dealerPrincipalEmailId)
 */
export async function findDealerLocally(dealerCode?: string, email?: string): Promise<DealerInfo | null> {
  try {
    // 1. Try Lookup by Dealer Code
    if (dealerCode) {
      const dealer = await getDealerByCode(dealerCode);
      if (dealer) return dealer;
    }

    // 2. Try Lookup by Email (checks both domainId and dealerPrincipalEmailId)
    if (email) {
      const dealer = await Dealer.findOne({
        where: {
          [Op.or]: [
            { domainId: { [Op.iLike]: email.toLowerCase() } as any },
            { dealerPrincipalEmailId: { [Op.iLike]: email.toLowerCase() } as any }
          ],
          isActive: true
        }
      });

      if (dealer) {
        // reuse same logic as getDealerByEmail for consistency
        return getDealerByEmail(dealer.domainId || dealer.dealerPrincipalEmailId || email);
      }
    }

    return null;
  } catch (error) {
    logger.error('[DealerService] Error in local dealer fallback lookup:', error);
    return null;
  }
}

