/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-console */

import { Request, Response } from 'express';
import { Types } from 'mongoose';
import crypto from 'crypto';
import catchAsync from '@/shared/utils/catchAsync';
import { Company } from '@/modules/company/company.model';
import User from '@/modules/user/user.model';
import { Contact } from '@/modules/contacts/contacts.model';
import { Activity } from '@/modules/activity/activity.model';
import { getObjectId } from '@/shared/utils/commonHelper';
import { SOCKET_EVENTS } from '@/shared/constants';
import { emitToUser, getIO } from '@/shared/socket/socket.service';
import { MyOperatorWebhookPayload, MyOperatorWebhookPayloadNew } from './myoperator.interface';
import { formatPhoneNumber } from './myoperator.helper';
import { ICompanyDoc } from '../company/company.interface';
import { WebhookRequest } from '../webhook/webhook.model';

/**
 * Store webhook request for debugging
 */
async function storeWebhookRequest(
  companyId: string | Types.ObjectId,
  req: Request,
  status: 'received' | 'processed' | 'failed',
  error?: string,
) {
  try {
    await WebhookRequest.create({
      companyId: companyId,
      method: req.method,
      url: req.originalUrl,
      headers: req.headers,
      body: req.body,
      query: req.query,
      params: req.params,
      ip: req.ip || req.connection?.remoteAddress,
      userAgent: req.get('User-Agent'),
      status,
      processingError: error,
    });
  } catch (err) {
    console.error('Error storing webhook request:', err);
  }
}

/**
 * Find or create contact by phone number
 */
async function findOrCreateContact(
  companyId: Types.ObjectId,
  phoneNumber: string,
  customerName?: string,
): Promise<Types.ObjectId | null> {
  try {
    // Format phone number
    const formattedPhone = formatPhoneNumber(phoneNumber, '91');

    // Try to find existing contact
    let contact = await Contact.findOne({
      company: companyId,
      'phone.number': formattedPhone,
    });

    // Create contact if not found and name is provided
    if (!contact && customerName) {
      const [firstName, ...lastNameParts] = customerName.trim().split(' ');
      contact = await Contact.create({
        firstName: firstName || 'Unknown',
        lastName: lastNameParts.join(' ') || '',
        phone: [
          {
            countryCode: '91',
            number: formattedPhone,
          },
        ],
        company: companyId,
        source: 'MyOperator',
      });
    }

    return contact?._id || null;
  } catch (error) {
    console.error('Error finding/creating contact:', error);
    return null;
  }
}

/**
 * Find user by MyOperator extension
 */
async function findUserByExtension(
  companyId: Types.ObjectId,
  extension: number,
): Promise<Types.ObjectId | null> {
  try {
    const user = await User.findOne({
      'company.id': companyId,
      'myoperator.extension': extension,
      status: 'active',
    });

    return user?._id || null;
  } catch (error) {
    console.error('Error finding user by extension:', error);
    return null;
  }
}

/**
 * Find all active leads by contact
 */
async function findLeadsByContact(
  contactId: Types.ObjectId,
): Promise<Types.ObjectId[]> {
  try {
    const { Lead } = await import('@/modules/lead/lead.model');

    const leads = await Lead.find({
      contact: contactId,
      isDeleted: false,
    })
      .select('_id')
      .sort({ createdAt: -1 });

    return leads.map(lead => lead._id);
  } catch (error) {
    console.error('Error finding leads by contact:', error);
    return [];
  }
}

/**
 * Parse duration string (HH:MM:SS) to seconds
 */
function parseDurationToSeconds(duration: string): number {
  try {
    const parts = duration.split(':').map(p => parseInt(p, 10));
    if (parts.length === 3) {
      return parts[0] * 3600 + parts[1] * 60 + parts[2];
    }
    return 0;
  } catch {
    return 0;
  }
}

/**
 * Normalize webhook payload from new format to old format
 */
function normalizeWebhookPayload(payload: any): MyOperatorWebhookPayload {
  // Check if it's the new format (has _ai field)
  if (payload._ai) {
    const newPayload = payload as MyOperatorWebhookPayloadNew;

    // Extract customer number (remove + and country code prefix)
    const customerNumber = newPayload._cr || newPayload._cl?.replace(/^\+/, '');

    // Extract agent information from call legs
    let agentExtension: number | undefined;
    let agentName: string | undefined;
    let callStatus = 'unknown';

    if (newPayload._ld && newPayload._ld.length > 0) {
      // Get the first call leg with agent info
      for (const leg of newPayload._ld) {
        if (leg._rr && leg._rr.length > 0) {
          const agent = leg._rr[0];
          agentExtension = agent._ex ? parseInt(agent._ex, 10) : undefined;
          agentName = agent._na;
          break;
        }
      }

      if (!callStatus || callStatus === 'unknown') {
        const allReceived = newPayload._ld.every(leg => leg._ac === 'received');
        callStatus = allReceived ? 'answered' : 'missed';
      }
    }

    // Parse duration
    const durationInSeconds = parseDurationToSeconds(newPayload._dr);

    // Determine call type based on available data
    let callType = 'incoming';
    if (newPayload._ty === 2) {
      callType = 'outgoing';
    }

    return {
      myoperator_company_id: newPayload._ci,
      unique_id: newPayload._cri || newPayload._ri, // _cri is reference_id used in click-to-call
      customer_number: customerNumber,
      customer_name: newPayload._dn || undefined,
      agent_extension: agentExtension,
      agent_name: agentName,
      call_type: callType,
      call_status: callStatus,
      duration: durationInSeconds,
      recording_file: newPayload._fn || undefined,
      timestamp: new Date(newPayload._ms || newPayload._ts * 1000).toISOString(),
      reference_id: newPayload._cri, // Store reference_id separately for click-to-call tracking
    };
  }

  // Return as-is if it's already in the old format
  return payload as MyOperatorWebhookPayload;
}

/**
 * Parse reference_id to extract leadId and contactId
 * Format: REF_123456_ABC_LEAD_leadId_CONTACT_contactId
 */
function parseReferenceId(referenceId: string): {
  leadId: string | null;
  contactId: string | null;
} {
  try {
    const leadMatch = referenceId.match(/_LEAD_([a-f0-9]+)/i);
    const contactMatch = referenceId.match(/_CONTACT_([a-f0-9]+)/i);

    return {
      leadId: leadMatch ? leadMatch[1] : null,
      contactId: contactMatch ? contactMatch[1] : null,
    };
  } catch (error) {
    console.error('Error parsing reference_id:', error);
    return { leadId: null, contactId: null };
  }
}

async function createOrUpdateCallActivity(
  companyId: Types.ObjectId,
  payload: MyOperatorWebhookPayload,
  contactId: Types.ObjectId | null,
  leadId: Types.ObjectId | null,
  assignedToId: Types.ObjectId | null,
  referenceId: string | undefined,
) {
  try {
    const callStatus = payload.call_status?.toLowerCase() || 'unknown';
    const duration = payload.duration || 0;
    const recordingFile = payload.recording_file;
    const isAnswered = callStatus === 'answered';
    const isNotAnswered = callStatus === 'missed' || callStatus === 'failed' || !isAnswered;

    let activityStatus: 'completed' | 'pending' = 'pending';
    if (isAnswered || isNotAnswered) {
      activityStatus = 'completed';
    }

    let title: string;
    let comment: string;
    let completionNote: string | undefined;

    if (isNotAnswered) {
      title = 'call - not answering';
      comment = '';
      completionNote = undefined;
    } else {
      let customerName = 'Unknown';
      if (contactId) {
        const contact = await Contact.findById(contactId).select('firstName lastName').lean();
        if (contact) {
          const parts = [contact.firstName, contact.lastName].filter(Boolean);
          customerName = parts.length ? parts.join(' ').trim() : 'Unknown';
        }
      }
      title = `MyOperator call with ${customerName}`;
      comment = `MyOperator call with ${customerName} - ${callStatus}`;
      completionNote = `Call status: ${callStatus}`;
      if (payload.agent_name) completionNote += `\nAgent: ${payload.agent_name}`;
    }

    if (referenceId) {
      const existing = await Activity.findOne({
        company: companyId,
        type: 'call',
        myoperatorReferenceId: referenceId,
      });
      if (existing) {
        await Activity.findByIdAndUpdate(existing._id, {
          status: activityStatus,
          duration,
          title,
          comment,
          myoperatorReferenceId: null,
          ...(recordingFile && { audioFileName: recordingFile }),
          ...(completionNote && { completionNote }),
          updatedBy: assignedToId || existing.assignedTo,
        });
        return existing;
      }
    }

    const baseActivityData = {
      company: companyId,
      contact: contactId || undefined,
      title,
      comment,
      type: 'call' as const,
      status: activityStatus,
      assignedTo: assignedToId || undefined,
      createdBy: assignedToId || undefined,
      updatedBy: assignedToId || undefined,
      lead: leadId || undefined,
      duration,
      myoperatorReferenceId: isNotAnswered ? null : undefined,
      ...(recordingFile && { audioFileName: recordingFile }),
      ...(completionNote && { completionNote }),
    };

    const activity = await Activity.create(baseActivityData);
    return activity;
  } catch (error) {
    console.error('Error creating/updating call activity:', error);
    throw error;
  }
}

export const handleMyOperatorWebhook = catchAsync(
  async (req: Request, res: Response) => {
    const rawPayload = req.body;

    const myOperatorCompanyId = rawPayload._ci;

    const company = await Company.findOne({
      'myoperator.companyId': myOperatorCompanyId
    }).select('_id myoperator');

    if (!company) {
      console.error(`Company with MyOperator company ID ${myOperatorCompanyId} not found`);
      await storeWebhookRequest(myOperatorCompanyId as string, req, 'failed', 'Company not found');
      return res.status(404).json({
        success: false,
        message: 'Company not found',
      });
    }

    console.log('Raw Payload:', JSON.stringify(rawPayload, null, 2));

    await storeWebhookRequest(company._id, req, 'received');

    res.status(200).json({
      success: true,
      message: 'Webhook received',
    });

    const normalizedPayload = normalizeWebhookPayload(rawPayload);

    void processWebhookInBackground(company, normalizedPayload, req).catch((err) => {
      console.error('MyOperator webhook background processing error:', err);
    });
  },
);

/**
 * Process webhook in background
 */
async function processWebhookInBackground(
  company: ICompanyDoc,
  payload: MyOperatorWebhookPayload,
  req: Request,
) {
  try {

    if (!company.myoperator?.isActive) {
      console.error(`MyOperator not active for company ${company._id.toString()}`);
      await storeWebhookRequest(
        company._id,
        req,
        'failed',
        'MyOperator not active',
      );
      return;
    }

    // Extract data from payload
    const customerNumber = payload.customer_number;
    const customerName = payload.customer_name;
    const agentExtension = payload.agent_extension;
    const referenceId = payload.reference_id;

    if (!customerNumber) {
      console.error('Customer number missing in webhook payload');
      await storeWebhookRequest(
        company._id,
        req,
        'failed',
        'Customer number missing',
      );
      return;
    }

    // Check if reference_id contains encoded leadId/contactId
    let specificLeadId: Types.ObjectId | null = null;
    let specificContactId: Types.ObjectId | null = null;

    if (referenceId) {
      const parsed = parseReferenceId(referenceId);

      if (parsed.leadId) {
        specificLeadId = getObjectId(parsed.leadId);
        console.log(`📌 Extracted leadId from reference_id: ${parsed.leadId}`);
      }

      if (parsed.contactId) {
        specificContactId = getObjectId(parsed.contactId);
        console.log(`📌 Extracted contactId from reference_id: ${parsed.contactId}`);
      }
    }


    // Find user by extension
    let assignedToId: Types.ObjectId | null = null;
    if (agentExtension) {
      assignedToId = await findUserByExtension(company._id, agentExtension);
    }

    const activity = await createOrUpdateCallActivity(
      company._id,
      payload,
      specificContactId,
      specificLeadId,
      assignedToId,
      referenceId,
    );

    await storeWebhookRequest(company._id, req, 'processed');

    if (getIO() && activity?.assignedTo) {
      emitToUser(activity.assignedTo.toString(), SOCKET_EVENTS.CALL_END, {
        companyId: company._id.toString(),
        referenceId: referenceId ?? undefined,
        userId: activity.assignedTo.toString(),
        call_status: payload.call_status,
        duration: payload.duration ?? undefined,
        activityId: activity._id?.toString(),
      });
    }

    console.log('✅ MyOperator webhook processed successfully');
  } catch (error: any) {
    console.error('Error processing MyOperator webhook:', error);
    await storeWebhookRequest(
      company._id,
      req,
      'failed',
      error.message || 'Unknown error',
    );
  }
}
