import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, EntityManager } from 'typeorm';
import { Company } from '../entities/company.entity';
import { AuditRequest } from '../../audit-requests/entities/audit-request.entity';

/**
 * CompanySyncService
 *
 * Handles automatic synchronization of company details when audit requests are created/updated
 * Implements "Latest Wins" policy: newest contact info always wins
 * Logs all changes in company_change_log table
 */
@Injectable()
export class CompanySyncService {
  private readonly logger = new Logger('CompanySyncService');

  constructor(
    @InjectRepository(Company, 'scheme_dbs')
    private readonly companyRepository: Repository<Company>,
  ) { }

  /**
   * SYNC FROM AUDIT REQUEST
   *
   * Called when an audit request is created/submitted
   * Extracts contact info from audit request and updates company record
   * Implements "Latest Wins" policy - newest info always used
   *
   * @param auditRequest - The newly created audit request
   * @param userId - User who created the request
   * @param manager - Entity manager for database operations
   */
  async syncFromAuditRequest(
    auditRequest: AuditRequest,
    userId: number,
    manager: EntityManager,
  ): Promise<void> {
    try {
      // ════════════════════════════════════════════════════════════
      // STEP 1: VALIDATE - Check company_id exists
      // ════════════════════════════════════════════════════════════

      if (!auditRequest.company_id) {
        this.logger.warn(
          `[SYNC] No company_id in audit request ${auditRequest.id}`,
        );
        return;
      }

      // ════════════════════════════════════════════════════════════
      // STEP 2: FIND COMPANY
      // ════════════════════════════════════════════════════════════

      const company = await manager.findOne(Company, {
        where: { id: auditRequest.company_id },
      });

      if (!company) {
        this.logger.warn(
          `[SYNC] Company #${auditRequest.company_id} not found`,
        );
        return;
      }

      // ════════════════════════════════════════════════════════════
      // STEP 3: EXTRACT NEW VALUES FROM AUDIT REQUEST
      // ════════════════════════════════════════════════════════════

      const newContactPerson = auditRequest.auditee_name;
      const newEmail = auditRequest.auditee_email;
      const newMobile = auditRequest.auditee_contact;
      const newAddress = auditRequest.location;
      // 🆕 ISSUE 8 FIX — Also sync scope_of_work from audit request
      const newScope = auditRequest.scope_of_work;

      // Track changes
      const changes: Array<{
        field: string;
        old_value: any;
        new_value: any;
      }> = [];

      // ════════════════════════════════════════════════════════════
      // STEP 4: LATEST WINS POLICY
      // Compare each field - update only if new value provided & different
      // ════════════════════════════════════════════════════════════

      // Field 1: contact_person (auditee_name)
      if (newContactPerson && newContactPerson !== company.contact_person) {
        changes.push({
          field: 'contact_person',
          old_value: company.contact_person,
          new_value: newContactPerson,
        });
        company.contact_person = newContactPerson;
        this.logger.log(
          `[SYNC] Company #${company.id}: contact_person "${company.contact_person}" → "${newContactPerson}"`,
        );
      }

      // Field 2: email
      if (newEmail && newEmail !== company.email) {
        changes.push({
          field: 'email',
          old_value: company.email,
          new_value: newEmail,
        });
        company.email = newEmail;
        this.logger.log(
          `[SYNC] Company #${company.id}: email "${company.email}" → "${newEmail}"`,
        );
      }

      // Field 3: mobile
      if (newMobile && newMobile !== company.mobile) {
        changes.push({
          field: 'mobile',
          old_value: company.mobile,
          new_value: newMobile,
        });
        company.mobile = newMobile;
        this.logger.log(
          `[SYNC] Company #${company.id}: mobile "${company.mobile}" → "${newMobile}"`,
        );
      }

      // Field 4: address
      if (newAddress && newAddress !== company.address) {
        changes.push({
          field: 'address',
          old_value: company.address,
          new_value: newAddress,
        });
        company.address = newAddress;
        this.logger.log(`[SYNC] Company #${company.id}: address updated`);
      }

      // 🆕 ISSUE 8 FIX — Field 5: scope_of_work
      // Same "latest wins" pattern. Only update if the audit request has a
      // non-empty scope AND it's different from what the company already has.
      if (newScope && newScope.trim() && newScope !== company.scope_of_work) {
        changes.push({
          field: 'scope_of_work',
          old_value: company.scope_of_work,
          new_value: newScope,
        });
        company.scope_of_work = newScope;
        this.logger.log(`[SYNC] Company #${company.id}: scope_of_work updated`);
      }

      // ════════════════════════════════════════════════════════════
      // STEP 5: SAVE COMPANY IF CHANGES EXIST
      // ════════════════════════════════════════════════════════════

      if (changes.length > 0) {
        // Update timestamp to current
        company.updated_at = new Date();

        // Save company to database
        await manager.save(Company, company);
        this.logger.log(
          `[SYNC] ✅ Company #${company.id} updated with ${changes.length} change(s)`,
        );

        // ════════════════════════════════════════════════════════════
        // STEP 6: LOG CHANGES IN company_change_log TABLE
        // ════════════════════════════════════════════════════════════

        for (const change of changes) {
          try {
            // Insert into change log using raw query
            // (in case CompanyChangeLog entity isn't configured)
            await manager.query(
              `INSERT INTO company_change_log 
               (company_id, field_name, old_value, new_value, source_audit_id, changed_by_user_id, created_at)
               VALUES (?, ?, ?, ?, ?, ?, NOW())`,
              [
                company.id,
                change.field,
                change.old_value,
                change.new_value,
                auditRequest.id,
                userId,
              ],
            );

            this.logger.log(
              `[SYNC] 📝 Logged change: ${change.field} → change_log`,
            );
          } catch (logError: any) {
            this.logger.warn(
              `[SYNC] Failed to log change for ${change.field}: ${logError?.message}`,
            );
            // Don't throw - continue even if logging fails
          }
        }

        this.logger.log(
          `[SYNC] ✅ Logged ${changes.length} change(s) to company_change_log`,
        );
      } else {
        this.logger.log(
          `[SYNC] ℹ️  No changes needed for company #${company.id} (all values same)`,
        );
      }
    } catch (error: any) {
      this.logger.error(
        `[SYNC] ❌ Error syncing company details: ${error?.message}`,
        error?.stack,
      );
      // Re-throw so audit-requests service can see the error
      throw error;
    }
  }

  /**
   * ALTERNATIVE METHOD: Sync from branch details
   *
   * Use this if you want more control over what gets synced
   * Useful for bulk updates or specific field changes
   *
   * @param company - Company to update
   * @param contactPerson - New contact person name
   * @param email - New email
   * @param mobile - New mobile number
   * @param auditRequestId - Source audit request
   * @param userId - User making change
   * @param manager - Entity manager
   */
  async syncFromBranch(
    company: Company,
    contactPerson: string | null,
    email: string | null,
    mobile: string | null,
    auditRequestId: number,
    userId: number,
    manager: EntityManager,
  ): Promise<void> {
    try {
      if (!company || !company.id) {
        this.logger.warn('[SYNC] Invalid company object');
        return;
      }

      const changes: Array<{
        field: string;
        old_value: any;
        new_value: any;
      }> = [];

      // Field 1: contact_person
      if (contactPerson && contactPerson !== company.contact_person) {
        changes.push({
          field: 'contact_person',
          old_value: company.contact_person,
          new_value: contactPerson,
        });
        company.contact_person = contactPerson;
      }

      // Field 2: email
      if (email && email !== company.email) {
        changes.push({
          field: 'email',
          old_value: company.email,
          new_value: email,
        });
        company.email = email;
      }

      // Field 3: mobile
      if (mobile && mobile !== company.mobile) {
        changes.push({
          field: 'mobile',
          old_value: company.mobile,
          new_value: mobile,
        });
        company.mobile = mobile;
      }

      // Save if changes exist
      if (changes.length > 0) {
        company.updated_at = new Date();
        await manager.save(Company, company);

        // Log all changes
        for (const change of changes) {
          try {
            await manager.query(
              `INSERT INTO company_change_log 
               (company_id, field_name, old_value, new_value, source_audit_id, changed_by_user_id, created_at)
               VALUES (?, ?, ?, ?, ?, ?, NOW())`,
              [
                company.id,
                change.field,
                change.old_value,
                change.new_value,
                auditRequestId,
                userId,
              ],
            );
          } catch (logError: any) {
            this.logger.warn(
              `[SYNC] Failed to log ${change.field}: ${logError?.message}`,
            );
          }
        }

        this.logger.log(
          `[SYNC] ✅ Company #${company.id} synced from branch with ${changes.length} change(s)`,
        );
      }
    } catch (error: any) {
      this.logger.error(`[SYNC] ❌ Error in syncFromBranch: ${error?.message}`);
      throw error;
    }
  }
}
