import prisma from '../../config/database';
import { AppError } from '../../shared/middleware/errorHandler';
import { AuditLogger } from '../../shared/utils/auditLogger';
import { Prisma } from '@prisma/client';

interface CreateContactData {
  type: string;
  name: string;
  nameAr?: string;
  email?: string;
  phone?: string;
  mobile?: string;
  website?: string;
  companyName?: string;
  companyNameAr?: string;
  taxNumber?: string;
  commercialRegister?: string;
  address?: string;
  city?: string;
  country?: string;
  postalCode?: string;
  categories?: string[];
  tags?: string[];
  parentId?: string;
  employeeId?: string | null;
  source: string;
  customFields?: any;
  createdById: string;
}

interface UpdateContactData extends Partial<CreateContactData> {
  status?: string;
  rating?: number;
}

interface SearchFilters {
  search?: string;
  type?: string;
  status?: string;
  category?: string;
  source?: string;
  rating?: number;
  createdFrom?: Date;
  createdTo?: Date;
  excludeCompanyEmployees?: boolean;
  excludeSuppliers?: boolean;
}

class ContactsService {
  async create(data: CreateContactData, userId: string) {
    // Check for duplicates based on email, phone, or tax number
    await this.checkDuplicates(data);

    // Validate employeeId if provided
    if (data.employeeId) {
      const employee = await prisma.employee.findUnique({
        where: { id: data.employeeId },
      });
      if (!employee) {
        throw new AppError(400, 'Employee not found - الموظف غير موجود');
      }
    }

    // Generate unique contact ID
    const uniqueContactId = await this.generateUniqueContactId();

    // Create contact
    const contact = await prisma.contact.create({
      data: {
        uniqueContactId,
        type: data.type,
        name: data.name,
        nameAr: data.nameAr,
        email: data.email,
        phone: data.phone,
        mobile: data.mobile,
        website: data.website,
        companyName: data.companyName,
        companyNameAr: data.companyNameAr,
        taxNumber: data.taxNumber,
        commercialRegister: data.commercialRegister,
        address: data.address,
        city: data.city,
        country: data.country,
        postalCode: data.postalCode,
        categories: data.categories ? {
          connect: data.categories.map(id => ({ id }))
        } : undefined,
        tags: data.tags || [],
        parentId: data.parentId,
        employeeId: data.employeeId || undefined,
        source: data.source,
        customFields: data.customFields || {},
        createdById: data.createdById,
      },
      include: {
        categories: true,
        parent: true,
        employee: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            uniqueEmployeeId: true,
          },
        },
        createdBy: {
          select: {
            id: true,
            email: true,
            username: true,
          },
        },
      },
    });

    // Log audit
    await AuditLogger.log({
      entityType: 'CONTACT',
      entityId: contact.id,
      action: 'CREATE',
      userId,
    });

    return contact;
  }

  async findAll(filters: SearchFilters, page: number = 1, pageSize: number = 20) {
    const skip = (page - 1) * pageSize;

    // Build where clause
    const where: Prisma.ContactWhereInput = {
      archivedAt: null, // Don't show archived contacts
    };

    if (filters.search) {
      where.OR = [
        { name: { contains: filters.search, mode: 'insensitive' } },
        { nameAr: { contains: filters.search, mode: 'insensitive' } },
        { email: { contains: filters.search, mode: 'insensitive' } },
        { phone: { contains: filters.search } },
        { mobile: { contains: filters.search } },
        { companyName: { contains: filters.search, mode: 'insensitive' } },
      ];
    }

    if (filters.type) {
      where.type = filters.type;
    }

    if (filters.status) {
      where.status = filters.status;
    }

    if (filters.source) {
      where.source = filters.source;
    }

    if (filters.rating !== undefined) {
      where.rating = filters.rating;
    }

    if (filters.category) {
      where.categories = {
        some: { id: filters.category }
      };
    }

    if (filters.excludeSuppliers) {
      where.NOT = [
        { type: 'SUPPLIER' },
        {
          categories: {
            some: {
              OR: [
                { name: { in: ['Supplier', 'Suppliers'] } },
                { nameAr: { contains: 'مورد' } },
              ],
            },
          },
        },
      ];
    }

    if (filters.createdFrom || filters.createdTo) {
      where.createdAt = {};
      if (filters.createdFrom) {
        where.createdAt.gte = filters.createdFrom;
      }
      if (filters.createdTo) {
        where.createdAt.lte = filters.createdTo;
      }
    }

    // Get total count
    const total = await prisma.contact.count({ where });

    // Get contacts
    const contacts = await prisma.contact.findMany({
      where,
      skip,
      take: pageSize,
      include: {
        categories: true,
        parent: {
          select: {
            id: true,
            name: true,
            type: true,
          },
        },
        employee: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            uniqueEmployeeId: true,
          },
        },
        createdBy: {
          select: {
            id: true,
            email: true,
            username: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return {
      contacts,
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

  async findById(id: string) {
    const contact = await prisma.contact.findUnique({
      where: { id },
      include: {
        categories: true,
        parent: true,
        employee: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            uniqueEmployeeId: true,
          },
        },
        children: true,
        relationships: {
          include: {
            toContact: {
              select: {
                id: true,
                name: true,
                type: true,
              },
            },
          },
        },
        relatedTo: {
          include: {
            fromContact: {
              select: {
                id: true,
                name: true,
                type: true,
              },
            },
          },
        },
        activities: {
          take: 20,
          orderBy: {
            createdAt: 'desc',
          },
        },
        deals: {
          take: 10,
          orderBy: {
            createdAt: 'desc',
          },
        },
        notes: {
          orderBy: {
            createdAt: 'desc',
          },
        },
        attachments: {
          orderBy: {
            uploadedAt: 'desc',
          },
        },
        createdBy: {
          select: {
            id: true,
            email: true,
            username: true,
          },
        },
      },
    });

    if (!contact) {
      throw new AppError(404, 'جهة الاتصال غير موجودة - Contact not found');
    }

    return contact;
  }

  async update(id: string, data: UpdateContactData, userId: string) {
    // Get existing contact
    const existing = await prisma.contact.findUnique({
      where: { id },
    });

    if (!existing) {
      throw new AppError(404, 'جهة الاتصال غير موجودة - Contact not found');
    }

    // Check for duplicates if email/phone/tax changed
    if (data.email || data.phone || data.taxNumber) {
      await this.checkDuplicates(data as CreateContactData, id);
    }

    // Validate employeeId if provided
    if (data.employeeId !== undefined && data.employeeId !== null) {
      const employee = await prisma.employee.findUnique({
        where: { id: data.employeeId },
      });
      if (!employee) {
        throw new AppError(400, 'Employee not found - الموظف غير موجود');
      }
    }

    // Update contact
    const contact = await prisma.contact.update({
  where: { id },
  data: {
    type: data.type,
    name: data.name,
    nameAr: data.nameAr,
    email: data.email === '' || data.email === undefined ? null : data.email,
    phone: data.phone,
    mobile: data.mobile,
    website: data.website,
    companyName: data.companyName,
    companyNameAr: data.companyNameAr,
    taxNumber: data.taxNumber,
    commercialRegister: data.commercialRegister,
    address: data.address,
    city: data.city,
    country: data.country,
    postalCode: data.postalCode,
    categories: data.categories
      ? {
          set: data.categories.map((id) => ({ id })),
        }
      : undefined,
    tags: data.tags,
    employeeId:
      data.employeeId !== undefined ? (data.employeeId || null) : undefined,
    source: data.source,
    status: data.status,
    rating: data.rating,
    customFields: data.customFields,
  },
  include: {
    categories: true,
    parent: true,
    employee: {
      select: {
        id: true,
        firstName: true,
        lastName: true,
        email: true,
        uniqueEmployeeId: true,
      },
    },
  },
});

    // Log audit
    await AuditLogger.log({
      entityType: 'CONTACT',
      entityId: contact.id,
      action: 'UPDATE',
      userId,
      changes: {
        before: existing,
        after: contact,
      },
    });

    return contact;
  }

  async archive(id: string, userId: string, reason?: string) {
    const contact = await prisma.contact.update({
      where: { id },
      data: {
        status: 'ARCHIVED',
        archivedAt: new Date(),
      },
    });

    await AuditLogger.log({
      entityType: 'CONTACT',
      entityId: contact.id,
      action: 'ARCHIVE',
      userId,
      reason,
    });

    return contact;
  }

  async delete(id: string, userId: string, reason: string) {
    // Hard delete - only for authorized users
    // This should be restricted at the controller level
    
    const contact = await prisma.contact.delete({
      where: { id },
    });

    await AuditLogger.log({
      entityType: 'CONTACT',
      entityId: id,
      action: 'DELETE',
      userId,
      reason,
    });

    return contact;
  }

  async merge(sourceId: string, targetId: string, userId: string, reason: string) {
    // Get both contacts
    const source = await prisma.contact.findUnique({ where: { id: sourceId } });
    const target = await prisma.contact.findUnique({ where: { id: targetId } });

    if (!source || !target) {
      throw new AppError(404, 'جهة الاتصال غير موجودة - Contact not found');
    }

    // Start transaction
    await prisma.$transaction(async (tx) => {
      // Update all related records to point to target
      await tx.deal.updateMany({
        where: { contactId: sourceId },
        data: { contactId: targetId },
      });

      await tx.activity.updateMany({
        where: { contactId: sourceId },
        data: { contactId: targetId },
      });

      await tx.note.updateMany({
        where: { contactId: sourceId },
        data: { contactId: targetId },
      });

      await tx.attachment.updateMany({
        where: { contactId: sourceId },
        data: { contactId: targetId },
      });

      // Archive source contact
      await tx.contact.update({
        where: { id: sourceId },
        data: {
          status: 'ARCHIVED',
          archivedAt: new Date(),
        },
      });
    });

    // Log audit
    await AuditLogger.log({
      entityType: 'CONTACT',
      entityId: targetId,
      action: 'MERGE',
      userId,
      reason,
      changes: {
        sourceId,
        targetId,
        sourceData: source,
      },
    });

    return target;
  }

  async addRelationship(
    fromContactId: string,
    toContactId: string,
    type: string,
    startDate: Date,
    userId: string,
    endDate?: Date,
    notes?: string
  ) {
    const relationship = await prisma.contactRelationship.create({
      data: {
        fromContactId,
        toContactId,
        type,
        startDate,
        endDate,
        notes,
      },
      include: {
        fromContact: {
          select: {
            id: true,
            uniqueContactId: true,
            type: true,
            name: true,
            email: true,
            phone: true,
          },
        },
        toContact: {
          select: {
            id: true,
            uniqueContactId: true,
            type: true,
            name: true,
            email: true,
            phone: true,
          },
        },
      },
    });

    await AuditLogger.log({
      entityType: 'CONTACT_RELATIONSHIP',
      entityId: relationship.id,
      action: 'CREATE',
      userId,
    });

    return relationship;
  }

  async getRelationships(contactId: string) {
    const relationships = await prisma.contactRelationship.findMany({
      where: {
        OR: [
          { fromContactId: contactId },
          { toContactId: contactId }
        ],
        isActive: true,
      },
      include: {
        fromContact: {
          select: {
            id: true,
            uniqueContactId: true,
            type: true,
            name: true,
            nameAr: true,
            email: true,
            phone: true,
            status: true,
          },
        },
        toContact: {
          select: {
            id: true,
            uniqueContactId: true,
            type: true,
            name: true,
            nameAr: true,
            email: true,
            phone: true,
            status: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    return relationships;
  }

  async updateRelationship(
    id: string,
    data: {
      type?: string;
      startDate?: Date;
      endDate?: Date;
      notes?: string;
      isActive?: boolean;
    },
    userId: string
  ) {
    const relationship = await prisma.contactRelationship.update({
      where: { id },
      data,
      include: {
        fromContact: {
          select: {
            id: true,
            uniqueContactId: true,
            name: true,
          },
        },
        toContact: {
          select: {
            id: true,
            uniqueContactId: true,
            name: true,
          },
        },
      },
    });

    await AuditLogger.log({
      entityType: 'CONTACT_RELATIONSHIP',
      entityId: relationship.id,
      action: 'UPDATE',
      userId,
      changes: data,
    });

    return relationship;
  }

  async deleteRelationship(id: string, userId: string) {
    // Soft delete by marking as inactive
    const relationship = await prisma.contactRelationship.update({
      where: { id },
      data: { isActive: false },
    });

    await AuditLogger.log({
      entityType: 'CONTACT_RELATIONSHIP',
      entityId: id,
      action: 'DELETE',
      userId,
    });

    return relationship;
  }

  async getHistory(id: string) {
    return AuditLogger.getEntityHistory('CONTACT', id);
  }

  // Import contacts from Excel/CSV
  async import(fileBuffer: Buffer, userId: string): Promise<{
    success: number;
    failed: number;
    duplicates: number;
    errors: Array<{ row: number; field: string; message: string; data?: any }>;
  }> {
    const xlsx = require('xlsx');
    const workbook = xlsx.read(fileBuffer, { type: 'buffer' });
    const sheetName = workbook.SheetNames[0];
    const worksheet = workbook.Sheets[sheetName];
    const data = xlsx.utils.sheet_to_json(worksheet);

    const results = {
      success: 0,
      failed: 0,
      duplicates: 0,
      errors: [] as Array<{ row: number; field: string; message: string; data?: any }>,
    };

    for (let i = 0; i < data.length; i++) {
      const row: any = data[i];
      const rowNumber = i + 2; // Excel rows start at 1, header is row 1

      try {
        // Validate required fields
        if (!row.name || !row.type || !row.source) {
          results.errors.push({
            row: rowNumber,
            field: !row.name ? 'name' : !row.type ? 'type' : 'source',
            message: 'Required field missing',
            data: row,
          });
          results.failed++;
          continue;
        }

        // Validate type
        if (!['INDIVIDUAL', 'COMPANY', 'HOLDING', 'GOVERNMENT',  'ORGANIZATION','EMBASSIES','BANK','UNIVERSITY','SCHOOL','UN','NGO','INSTITUTION',].includes(row.type)) {
          results.errors.push({
            row: rowNumber,
            field: 'type',
            message: 'Invalid type. Must be INDIVIDUAL, COMPANY, HOLDING, or GOVERNMENT',
            data: row,
          });
          results.failed++;
          continue;
        }

        // Check for duplicates
        try {
          const contactData: CreateContactData = {
            type: row.type,
            name: row.name,
            nameAr: row.nameAr || row.name_ar,
            email: row.email,
            phone: row.phone,
            mobile: row.mobile,
            website: row.website,
            companyName: row.companyName || row.company_name,
            companyNameAr: row.companyNameAr || row.company_name_ar,
            taxNumber: row.taxNumber || row.tax_number,
            commercialRegister: row.commercialRegister || row.commercial_register,
            address: row.address,
            city: row.city,
            country: row.country,
            postalCode: row.postalCode || row.postal_code,
            source: row.source,
            tags: row.tags ? (typeof row.tags === 'string' ? row.tags.split(',').map((t: string) => t.trim()) : row.tags) : [],
            customFields: {},
            createdById: userId,
          };

          await this.checkDuplicates(contactData);
          
          // Create contact
          await this.create(contactData, userId);
          results.success++;
        } catch (error: any) {
          if (error.statusCode === 409) {
            results.duplicates++;
            results.errors.push({
              row: rowNumber,
              field: 'duplicate',
              message: error.message,
              data: row,
            });
          } else {
            throw error;
          }
        }
      } catch (error: any) {
        results.failed++;
        results.errors.push({
          row: rowNumber,
          field: 'general',
          message: error.message || 'Unknown error',
          data: row,
        });
      }
    }

    return results;
  }

  // Export contacts to Excel
  async export(filters: SearchFilters): Promise<Buffer> {
    const xlsx = require('xlsx');
    
    // Build query
    const where: Prisma.ContactWhereInput = {
      status: { not: 'DELETED' },
    };
    const notConditions: Prisma.ContactWhereInput[] = [];

    if (filters.search) {
      where.OR = [
        { name: { contains: filters.search, mode: 'insensitive' } },
        { email: { contains: filters.search, mode: 'insensitive' } },
        { companyName: { contains: filters.search, mode: 'insensitive' } },
      ];
    }

    if (filters.type) where.type = filters.type;
    if (filters.status) where.status = filters.status;
    if (filters.source) where.source = filters.source;
    if (filters.rating) where.rating = filters.rating;

    if (filters.excludeSuppliers) {
      notConditions.push(
        { type: 'SUPPLIER' },
        {
          categories: {
            some: {
              OR: [
                { name: { in: ['Supplier', 'Suppliers'] } },
                { nameAr: { contains: 'مورد' } },
              ],
            },
          },
        }
      );
    }

    if (filters.excludeCompanyEmployees) {
      const companyEmployeeCategory = await prisma.contactCategory.findFirst({
        where: { name: 'Company Employee', isActive: true },
      });
      if (companyEmployeeCategory) {
        notConditions.push({
          categories: {
            some: { id: companyEmployeeCategory.id },
          },
        });
      }
    }

    if (notConditions.length > 0) {
      where.NOT = notConditions;
    }

    // Fetch all contacts (no pagination for export)
    const contacts = await prisma.contact.findMany({
      where,
      include: {
        categories: true,
        parent: {
          select: {
            name: true,
          },
        },
        createdBy: {
          select: {
            username: true,
            email: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    // Transform data for Excel
    const exportData = contacts.map(contact => ({
      'Contact ID': contact.uniqueContactId,
      'Type': contact.type,
      'Name': contact.name,
      'Name (Arabic)': contact.nameAr || '',
      'Email': contact.email || '',
      'Phone': contact.phone || '',
      'Mobile': contact.mobile || '',
      'Website': contact.website || '',
      'Company Name': contact.companyName || '',
      'Company Name (Arabic)': contact.companyNameAr || '',
      'Tax Number': contact.taxNumber || '',
      'Commercial Register': contact.commercialRegister || '',
      'Address': contact.address || '',
      'City': contact.city || '',
      'Country': contact.country || '',
      'Postal Code': contact.postalCode || '',
      'Source': contact.source,
      'Rating': contact.rating || '',
      'Status': contact.status,
      'Tags': contact.tags?.join(', ') || '',
      'Categories': contact.categories?.map((c: any) => c.name).join(', ') || '',
      'Parent Company': contact.parent?.name || '',
      'Created By': contact.createdBy?.username || '',
      'Created At': contact.createdAt.toISOString(),
    }));

    // Create workbook and worksheet
    const worksheet = xlsx.utils.json_to_sheet(exportData);
    const workbook = xlsx.utils.book_new();
    xlsx.utils.book_append_sheet(workbook, worksheet, 'Contacts');

    // Set column widths
    const columnWidths = [
      { wch: 15 }, // Contact ID
      { wch: 12 }, // Type
      { wch: 25 }, // Name
      { wch: 25 }, // Name (Arabic)
      { wch: 30 }, // Email
      { wch: 15 }, // Phone
      { wch: 15 }, // Mobile
      { wch: 30 }, // Website
      { wch: 25 }, // Company Name
      { wch: 25 }, // Company Name (Arabic)
      { wch: 20 }, // Tax Number
      { wch: 20 }, // Commercial Register
      { wch: 30 }, // Address
      { wch: 15 }, // City
      { wch: 15 }, // Country
      { wch: 12 }, // Postal Code
      { wch: 15 }, // Source
      { wch: 8 },  // Rating
      { wch: 10 }, // Status
      { wch: 30 }, // Tags
      { wch: 30 }, // Categories
      { wch: 25 }, // Parent Company
      { wch: 15 }, // Created By
      { wch: 20 }, // Created At
    ];
    worksheet['!cols'] = columnWidths;

    // Generate buffer
    const buffer = xlsx.write(workbook, { type: 'buffer', bookType: 'xlsx' });
    return buffer;
  }

  // Check for potential duplicates (public method for API endpoint)
  async findDuplicates(data: Partial<CreateContactData>, excludeId?: string) {
    const conditions: Prisma.ContactWhereInput[] = [];

    if (data.email) {
      conditions.push({ email: data.email });
    }

    if (data.phone) {
      conditions.push({ phone: data.phone });
    }

    if (data.mobile) {
      conditions.push({ mobile: data.mobile });
    }

    if (data.taxNumber) {
      conditions.push({ taxNumber: data.taxNumber });
    }

    if (data.commercialRegister) {
      conditions.push({ commercialRegister: data.commercialRegister });
    }

    if (conditions.length === 0) return [];

    const where: Prisma.ContactWhereInput = {
      OR: conditions,
      status: { not: 'DELETED' },
    };

    if (excludeId) {
      where.NOT = { id: excludeId };
    }

    const duplicates = await prisma.contact.findMany({
      where,
      select: {
        id: true,
        uniqueContactId: true,
        type: true,
        name: true,
        nameAr: true,
        email: true,
        phone: true,
        mobile: true,
        taxNumber: true,
        commercialRegister: true,
        status: true,
        createdAt: true,
      },
      take: 10, // Limit to 10 potential duplicates
    });

    return duplicates;
  }

  // Private helper methods
  private async checkDuplicates(data: CreateContactData, excludeId?: string) {
    const duplicates = await this.findDuplicates(data, excludeId);
    
    if (duplicates.length > 0) {
      throw new AppError(
        409,
        `جهة اتصال مكررة - Duplicate contact found: ${duplicates[0].name}`
      );
    }
  }

  private async generateUniqueContactId(): Promise<string> {
    const year = new Date().getFullYear();
    const prefix = `CNT-${year}-`;
    
    // Get the last contact for this year
    const lastContact = await prisma.contact.findFirst({
      where: {
        uniqueContactId: {
          startsWith: prefix,
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
      select: {
        uniqueContactId: true,
      },
    });

    let nextNumber = 1;
    if (lastContact) {
      const lastNumber = parseInt(lastContact.uniqueContactId.split('-')[2]);
      nextNumber = lastNumber + 1;
    }

    return `${prefix}${nextNumber.toString().padStart(6, '0')}`;
  }
}

export const contactsService = new ContactsService();

