import db from '../config/database';
import { logger } from '../utils/logger';
import {
  Certificate,
  CertificateCreateInput,
  CertificateUpdateInput,
  CertificateQueryOptions,
  CertificateType,
  CertificateStatus,
} from '../models/Certificate';

export interface CertificateDatabaseRecord {
  id: string;
  tenantId: string;
  shopId: string;
  taskId?: string;
  traceId: string;
  businessType: 'TOC' | 'TOB';
  certificateName: string;
  certificateType: CertificateType;
  certificateNo: string;
  productId?: string;
  productName?: string;
  issuer: string;
  issueDate: Date;
  expiryDate: Date;
  status: CertificateStatus;
  fileUrl?: string;
  fileHash?: string;
  remarks?: string;
  createdAt: Date;
  updatedAt: Date;
}

export interface CertificateBackupData {
  version: string;
  exportedAt: Date;
  tenantId: string;
  totalRecords: number;
  certificates: CertificateDatabaseRecord[];
}

export interface CertificateMigrationResult {
  success: boolean;
  totalMigrated: number;
  failedRecords: string[];
  errors: string[];
  traceId: string;
}

export interface CertificateStatistics {
  totalCertificates: number;
  byStatus: Record<CertificateStatus, number>;
  byType: Record<CertificateType, number>;
  expiringWithin30Days: number;
  expired: number;
  byPlatform: Record<string, number>;
}

export interface BulkImportResult {
  success: number;
  failed: number;
  errors: Array<{ row: number; error: string }>;
}

export class CertificateDatabaseService {
  private static readonly TABLE_NAME = 'cf_certificate';
  private static readonly BACKUP_TABLE = 'cf_certificate_backup';
  private static readonly VERSION = '1.0.0';

  static async initTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.TABLE_NAME);
    if (!hasTable) {
      logger.info(`[CertificateDatabaseService] Creating ${this.TABLE_NAME} table...`);
      await db.schema.createTable(this.TABLE_NAME, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 64).notNullable().index();
        table.string('shop_id', 64).notNullable().index();
        table.string('task_id', 36);
        table.string('trace_id', 64).notNullable();
        table.enum('business_type', ['TOC', 'TOB']).notNullable();
        table.string('certificate_name', 255).notNullable();
        table.enum('certificate_type', [
          'CE', 'FCC', 'ROHS', 'ISO9001', 'ISO14001',
          'FDA', 'UL', 'CCC', 'EXPORT_LICENSE', 'ORIGIN', 'OTHER'
        ]).notNullable();
        table.string('certificate_no', 128).notNullable();
        table.string('product_id', 64);
        table.string('product_name', 255);
        table.string('issuer', 255).notNullable();
        table.date('issue_date').notNullable();
        table.date('expiry_date').notNullable().index();
        table.enum('status', [
          'PENDING_REVIEW', 'APPROVED', 'REJECTED', 'EXPIRED', 'REVOKED'
        ]).defaultTo('PENDING_REVIEW').notNullable();
        table.string('file_url', 512);
        table.string('file_hash', 128);
        table.text('remarks');
        table.timestamp('created_at').defaultTo(db.fn.now());
        table.timestamp('updated_at').defaultTo(db.fn.now());

        table.unique(['tenant_id', 'certificate_no'], 'uk_cert_no');
        table.index(['certificate_type'], 'idx_cert_type');
        table.index(['status'], 'idx_cert_status');
        table.index(['product_id'], 'idx_cert_product');
      });
      logger.info(`[CertificateDatabaseService] Table ${this.TABLE_NAME} created`);
    }

    const hasBackupTable = await db.schema.hasTable(this.BACKUP_TABLE);
    if (!hasBackupTable) {
      await db.schema.createTable(this.BACKUP_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 64).notNullable().index();
        table.json('backup_data').notNullable();
        table.string('version', 32).notNullable();
        table.timestamp('created_at').defaultTo(db.fn.now());
      });
      logger.info(`[CertificateDatabaseService] Backup table ${this.BACKUP_TABLE} created`);
    }
  }

  static async insert(record: CertificateDatabaseRecord): Promise<string> {
    const id = record.id || this.generateId();
    
    await db(this.TABLE_NAME).insert({
      id,
      tenant_id: record.tenantId,
      shop_id: record.shopId,
      task_id: record.taskId || null,
      trace_id: record.traceId,
      business_type: record.businessType,
      certificate_name: record.certificateName,
      certificate_type: record.certificateType,
      certificate_no: record.certificateNo,
      product_id: record.productId || null,
      product_name: record.productName || null,
      issuer: record.issuer,
      issue_date: record.issueDate,
      expiry_date: record.expiryDate,
      status: record.status,
      file_url: record.fileUrl || null,
      file_hash: record.fileHash || null,
      remarks: record.remarks || null,
    });

    logger.info(`[CertificateDatabaseService] Certificate inserted: id=${id}, tenantId=${record.tenantId}, traceId=${record.traceId}`);
    return id;
  }

  static async update(
    tenantId: string,
    id: string,
    updates: Partial<CertificateDatabaseRecord>,
    traceId: string
  ): Promise<boolean> {
    const updateData: Record<string, any> = {
      updated_at: new Date(),
    };

    if (updates.certificateName) updateData.certificate_name = updates.certificateName;
    if (updates.certificateType) updateData.certificate_type = updates.certificateType;
    if (updates.certificateNo) updateData.certificate_no = updates.certificateNo;
    if (updates.productId !== undefined) updateData.product_id = updates.productId || null;
    if (updates.productName !== undefined) updateData.product_name = updates.productName || null;
    if (updates.issuer) updateData.issuer = updates.issuer;
    if (updates.issueDate) updateData.issue_date = updates.issueDate;
    if (updates.expiryDate) updateData.expiry_date = updates.expiryDate;
    if (updates.status) updateData.status = updates.status;
    if (updates.fileUrl !== undefined) updateData.file_url = updates.fileUrl || null;
    if (updates.fileHash !== undefined) updateData.file_hash = updates.fileHash || null;
    if (updates.remarks !== undefined) updateData.remarks = updates.remarks || null;

    const result = await db(this.TABLE_NAME)
      .where({ id, tenant_id: tenantId })
      .update(updateData);

    logger.info(`[CertificateDatabaseService] Certificate updated: id=${id}, tenantId=${tenantId}, traceId=${traceId}`);
    return result > 0;
  }

  static async delete(tenantId: string, id: string, traceId: string): Promise<boolean> {
    const result = await db(this.TABLE_NAME)
      .where({ id, tenant_id: tenantId })
      .delete();

    logger.info(`[CertificateDatabaseService] Certificate deleted: id=${id}, tenantId=${tenantId}, traceId=${traceId}`);
    return result > 0;
  }

  static async getById(tenantId: string, id: string): Promise<CertificateDatabaseRecord | null> {
    const row = await db(this.TABLE_NAME)
      .where({ id, tenant_id: tenantId })
      .first();

    return row ? this.mapRowToRecord(row) : null;
  }

  static async query(options: CertificateQueryOptions): Promise<CertificateDatabaseRecord[]> {
    let query = db(this.TABLE_NAME).where('tenant_id', options.tenantId);

    if (options.shopId) {
      query = query.andWhere('shop_id', options.shopId);
    }
    if (options.certificateType) {
      query = query.andWhere('certificate_type', options.certificateType);
    }
    if (options.status) {
      query = query.andWhere('status', options.status);
    }
    if (options.productId) {
      query = query.andWhere('product_id', options.productId);
    }
    if (options.searchKeyword) {
      query = query.andWhere(function() {
        this.where('certificate_name', 'like', `%${options.searchKeyword}%`)
          .orWhere('certificate_no', 'like', `%${options.searchKeyword}%`)
          .orWhere('product_name', 'like', `%${options.searchKeyword}%`);
      });
    }
    if (options.expiryDateFrom) {
      query = query.andWhere('expiry_date', '>=', options.expiryDateFrom);
    }
    if (options.expiryDateTo) {
      query = query.andWhere('expiry_date', '<=', options.expiryDateTo);
    }

    const page = options.page || 1;
    const pageSize = options.pageSize || 20;
    const offset = (page - 1) * pageSize;

    query = query.orderBy('created_at', 'desc').limit(pageSize).offset(offset);

    const rows = await query;
    return rows.map(this.mapRowToRecord);
  }

  static async count(options: CertificateQueryOptions): Promise<number> {
    let query = db(this.TABLE_NAME).where('tenant_id', options.tenantId);

    if (options.shopId) {
      query = query.andWhere('shop_id', options.shopId);
    }
    if (options.certificateType) {
      query = query.andWhere('certificate_type', options.certificateType);
    }
    if (options.status) {
      query = query.andWhere('status', options.status);
    }
    if (options.productId) {
      query = query.andWhere('product_id', options.productId);
    }

    const result = await query.count('* as count').first();
    return Number(result?.count || 0);
  }

  static async getStatistics(tenantId: string, shopId?: string): Promise<CertificateStatistics> {
    let query = db(this.TABLE_NAME).where('tenant_id', tenantId);
    if (shopId) {
      query = query.andWhere('shop_id', shopId);
    }

    const records = await query;

    const stats: CertificateStatistics = {
      totalCertificates: records.length,
      byStatus: {
        PENDING_REVIEW: 0,
        APPROVED: 0,
        REJECTED: 0,
        EXPIRED: 0,
        REVOKED: 0,
      },
      byType: {
        CE: 0,
        FCC: 0,
        ROHS: 0,
        ISO9001: 0,
        ISO14001: 0,
        FDA: 0,
        UL: 0,
        CCC: 0,
        EXPORT_LICENSE: 0,
        ORIGIN: 0,
        OTHER: 0,
      },
      expiringWithin30Days: 0,
      expired: 0,
      byPlatform: {},
    };

    const now = new Date();
    const thirtyDaysLater = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);

    for (const record of records) {
      stats.byStatus[record.status as CertificateStatus]++;
      stats.byType[record.certificate_type as CertificateType]++;

      const expiryDate = new Date(record.expiry_date);
      if (expiryDate < now) {
        stats.expired++;
      } else if (expiryDate <= thirtyDaysLater) {
        stats.expiringWithin30Days++;
      }
    }

    return stats;
  }

  static async backup(tenantId: string, traceId: string): Promise<string> {
    const records = await db(this.TABLE_NAME).where('tenant_id', tenantId);
    
    const backupData: CertificateBackupData = {
      version: this.VERSION,
      exportedAt: new Date(),
      tenantId,
      totalRecords: records.length,
      certificates: records.map(this.mapRowToRecord),
    };

    const backupId = this.generateBackupId();
    
    await db(this.BACKUP_TABLE).insert({
      id: backupId,
      tenant_id: tenantId,
      backup_data: JSON.stringify(backupData),
      version: this.VERSION,
    });

    logger.info(`[CertificateDatabaseService] Backup created: backupId=${backupId}, tenantId=${tenantId}, records=${records.length}, traceId=${traceId}`);
    return backupId;
  }

  static async restore(
    tenantId: string,
    backupId: string,
    traceId: string
  ): Promise<CertificateMigrationResult> {
    const result: CertificateMigrationResult = {
      success: false,
      totalMigrated: 0,
      failedRecords: [],
      errors: [],
      traceId,
    };

    try {
      const backup = await db(this.BACKUP_TABLE)
        .where({ id: backupId, tenant_id: tenantId })
        .first();

      if (!backup) {
        result.errors.push('Backup not found');
        return result;
      }

      const backupData: CertificateBackupData = JSON.parse(backup.backup_data);

      for (const cert of backupData.certificates) {
        try {
          const existing = await this.getById(tenantId, cert.id);
          
          if (existing) {
            await this.update(tenantId, cert.id, cert, traceId);
          } else {
            await this.insert(cert);
          }
          
          result.totalMigrated++;
        } catch (error: any) {
          result.failedRecords.push(cert.id);
          result.errors.push(`Failed to restore certificate ${cert.id}: ${error.message}`);
        }
      }

      result.success = result.failedRecords.length === 0;
      logger.info(`[CertificateDatabaseService] Restore completed: backupId=${backupId}, migrated=${result.totalMigrated}, traceId=${traceId}`);
    } catch (error: any) {
      result.errors.push(`Restore failed: ${error.message}`);
    }

    return result;
  }

  static async exportToJson(tenantId: string, traceId: string): Promise<CertificateBackupData> {
    const records = await db(this.TABLE_NAME).where('tenant_id', tenantId);
    
    const exportData: CertificateBackupData = {
      version: this.VERSION,
      exportedAt: new Date(),
      tenantId,
      totalRecords: records.length,
      certificates: records.map(this.mapRowToRecord),
    };

    logger.info(`[CertificateDatabaseService] Export completed: tenantId=${tenantId}, records=${records.length}, traceId=${traceId}`);
    return exportData;
  }

  static async bulkImport(
    tenantId: string,
    certificates: CertificateDatabaseRecord[],
    traceId: string
  ): Promise<BulkImportResult> {
    const result: BulkImportResult = {
      success: 0,
      failed: 0,
      errors: [],
    };

    for (let i = 0; i < certificates.length; i++) {
      const cert = certificates[i];
      try {
        cert.tenantId = tenantId;
        await this.insert(cert);
        result.success++;
      } catch (error: any) {
        result.failed++;
        result.errors.push({ row: i + 1, error: error.message });
      }
    }

    logger.info(`[CertificateDatabaseService] Bulk import completed: success=${result.success}, failed=${result.failed}, traceId=${traceId}`);
    return result;
  }

  static async getExpiringCertificates(
    tenantId: string,
    days: number,
    traceId: string
  ): Promise<CertificateDatabaseRecord[]> {
    const now = new Date();
    const futureDate = new Date(now.getTime() + days * 24 * 60 * 60 * 1000);

    const records = await db(this.TABLE_NAME)
      .where('tenant_id', tenantId)
      .where('status', 'APPROVED')
      .where('expiry_date', '>=', now)
      .where('expiry_date', '<=', futureDate)
      .orderBy('expiry_date', 'asc');

    logger.info(`[CertificateDatabaseService] Found ${records.length} expiring certificates within ${days} days, traceId=${traceId}`);
    return records.map(this.mapRowToRecord);
  }

  static async updateExpiredStatus(tenantId: string, traceId: string): Promise<number> {
    const now = new Date();
    
    const result = await db(this.TABLE_NAME)
      .where('tenant_id', tenantId)
      .where('status', 'APPROVED')
      .where('expiry_date', '<', now)
      .update({ status: 'EXPIRED', updated_at: now });

    logger.info(`[CertificateDatabaseService] Updated ${result} certificates to EXPIRED status, tenantId=${tenantId}, traceId=${traceId}`);
    return result;
  }

  static async getBackups(tenantId: string): Promise<Array<{ id: string; version: string; createdAt: Date }>> {
    const backups = await db(this.BACKUP_TABLE)
      .where('tenant_id', tenantId)
      .orderBy('created_at', 'desc')
      .select('id', 'version', 'created_at');

    return backups.map(b => ({
      id: b.id,
      version: b.version,
      createdAt: b.created_at,
    }));
  }

  static async deleteBackup(tenantId: string, backupId: string, traceId: string): Promise<boolean> {
    const result = await db(this.BACKUP_TABLE)
      .where({ id: backupId, tenant_id: tenantId })
      .delete();

    logger.info(`[CertificateDatabaseService] Backup deleted: backupId=${backupId}, tenantId=${tenantId}, traceId=${traceId}`);
    return result > 0;
  }

  private static generateId(): string {
    return `cert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private static generateBackupId(): string {
    return `backup_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private static mapRowToRecord(row: Record<string, any>): CertificateDatabaseRecord {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      shopId: row.shop_id,
      taskId: row.task_id,
      traceId: row.trace_id,
      businessType: row.business_type,
      certificateName: row.certificate_name,
      certificateType: row.certificate_type,
      certificateNo: row.certificate_no,
      productId: row.product_id,
      productName: row.product_name,
      issuer: row.issuer,
      issueDate: row.issue_date,
      expiryDate: row.expiry_date,
      status: row.status,
      fileUrl: row.file_url,
      fileHash: row.file_hash,
      remarks: row.remarks,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }
}
