import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { PageSeoAuditLog } from './entities/page_seo_audit_log.entity';

@Injectable()
export class PageSeoAuditService {
  constructor(
    @InjectRepository(PageSeoAuditLog)
    private readonly auditRepository: Repository<PageSeoAuditLog>,
  ) {}

  /**
   * Logs a change to the audit table.
   */
  async logChange(params: {
    seo_id: number;
    action_type:
      | 'CREATE'
      | 'UPDATE'
      | 'DELETE'
      | 'STATUS_CHANGE'
      | 'TOGGLE_ACTIVE';
    oldData?: any;
    newData?: any;
    user_id: number;
  }) {
    try {
      const { seo_id, action_type, oldData, newData, user_id } = params;

      let changes = null;
      if (
        action_type === 'UPDATE' ||
        action_type === 'STATUS_CHANGE' ||
        action_type === 'TOGGLE_ACTIVE'
      ) {
        changes = this.generateDiff(oldData, newData);
        if (!changes && action_type === 'UPDATE') return; // Don't log empty updates
      } else if (action_type === 'CREATE') {
        // For CREATE, we can optionally log the full initial state or leave as null
        changes = { _initial: 'Record Created' };
      }

      const log = this.auditRepository.create({
        seo_id,
        action_type,
        changes,
        user_id,
      });

      await this.auditRepository.save(log);
    } catch (error) {
      console.error('Failed to log SEO audit:', error);
      // Fail-safe: don't break the main SEO transaction
    }
  }

  /**
   * Computes the difference between two data objects.
   * Only includes fields that have actually changed.
   */
  private generateDiff(oldData: any, newData: any): any {
    if (!oldData || !newData) return null;

    const diff: any = {};
    const ignoredFields = [
      'created_at',
      'updated_at',
      'created_by',
      'updated_by',
      'id',
    ];

    const allKeys = new Set([...Object.keys(oldData), ...Object.keys(newData)]);

    allKeys.forEach((key) => {
      if (ignoredFields.includes(key)) return;

      const oldVal = oldData[key];
      const newVal = newData[key];

      // Deep compare for JSON fields (like meta_keywords)
      const isOldObj = typeof oldVal === 'object' && oldVal !== null;
      const isNewObj = typeof newVal === 'object' && newVal !== null;

      if (isOldObj && isNewObj) {
        if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
          diff[key] = { old: oldVal, new: newVal };
        }
      } else if (oldVal !== newVal) {
        diff[key] = { old: oldVal, new: newVal };
      }
    });

    return Object.keys(diff).length > 0 ? diff : null;
  }

  /**
   * Retrieves change history for a given SEO record.
   * Manually joins with users table to align with project architecture.
   */
  async getHistory(seoId: number, limit = 20, offset = 0) {
    const query = this.auditRepository
      .createQueryBuilder('audit')
      .leftJoin('users', 'u', 'audit.user_id = u.id')
      .where('audit.seo_id = :seoId', { seoId })
      .select([
        'audit.id as id',
        'audit.action_type as action_type',
        'audit.changes as changes',
        'audit.created_at as created_at',
        'u.id as user_id',
        'u.full_name as user_name',
        'u.role as user_role',
      ])
      .orderBy('audit.created_at', 'DESC')
      .limit(limit)
      .offset(offset);

    const logs = await query.getRawMany();

    // Map back to expected structure for consistency
    return logs.map((l) => ({
      ...l,
      user: {
        id: l.user_id,
        fullName: l.user_name,
        role: l.user_role,
      },
    }));
  }
}
