import db from '../config/database';
import { logger } from '../utils/logger';
import {
  ComplianceRule,
  ComplianceRuleCreateInput,
  ComplianceRuleQueryOptions,
  PlatformType,
  ProductCategory,
  CertificateType,
  ComplianceLevel,
} from '../models/ComplianceRule';

export interface ComplianceRuleDatabaseRecord {
  id: string;
  tenantId?: string;
  platform: PlatformType;
  productCategory: ProductCategory;
  targetMarket?: string;
  requiredCertificates: CertificateType[];
  complianceLevel: ComplianceLevel;
  description?: string;
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
}

export interface RuleBackupData {
  version: string;
  exportedAt: Date;
  tenantId?: string;
  totalRecords: number;
  rules: ComplianceRuleDatabaseRecord[];
}

export interface RuleStatistics {
  totalRules: number;
  activeRules: number;
  byPlatform: Record<PlatformType, number>;
  byCategory: Record<ProductCategory, number>;
  byComplianceLevel: Record<ComplianceLevel, number>;
  globalRules: number;
  tenantRules: number;
}

export interface RuleValidationResult {
  isValid: boolean;
  errors: string[];
  warnings: string[];
}

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

export interface RuleConflict {
  existingRule: ComplianceRuleDatabaseRecord;
  newRule: ComplianceRuleDatabaseRecord;
  conflictType: 'PLATFORM_CATEGORY' | 'CERTIFICATES' | 'LEVEL';
}

export class ComplianceRuleDatabaseService {
  private static readonly TABLE_NAME = 'cf_compliance_rule';
  private static readonly BACKUP_TABLE = 'cf_compliance_rule_backup';
  private static readonly VERSION = '1.0.0';

  private static readonly PLATFORM_CERTIFICATE_REQUIREMENTS: Partial<Record<PlatformType, CertificateType[]>> = {
    AMAZON: ['CE', 'FCC', 'ROHS'],
    EBAY: ['CE', 'FCC'],
    SHOPEE: ['CE', 'ROHS'],
    ALIEXPRESS: ['CE', 'ROHS'],
    TIKTOK: ['CE', 'FCC'],
    TEMU: ['CE', 'ROHS'],
  };

  private static readonly CATEGORY_CERTIFICATE_REQUIREMENTS: Partial<Record<ProductCategory, CertificateType[]>> = {
    ELECTRONICS: ['CE', 'FCC', 'ROHS'],
    TOYS: ['CE', 'EN71'],
    COSMETICS: ['FDA', 'GMP'],
    FOOD: ['FDA', 'HACCP'],
    MEDICAL: ['FDA', 'CE', 'ISO13485'],
    TEXTILES: ['OEKO_TEX'],
    FURNITURE: ['FSC'],
    AUTOMOTIVE: ['E_MARK', 'DOT'],
  };

  static async initTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.TABLE_NAME);
    if (!hasTable) {
      logger.info(`[ComplianceRuleDatabaseService] Creating ${this.TABLE_NAME} table...`);
      await db.schema.createTable(this.TABLE_NAME, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 64).index();
        table.enum('platform', [
          'AMAZON', 'EBAY', 'SHOPEE', 'ALIEXPRESS', 'TIKTOK', 'TEMU'
        ]).notNullable().index();
        table.enum('product_category', [
          'ELECTRONICS', 'TOYS', 'COSMETICS', 'FOOD', 'MEDICAL',
          'TEXTILES', 'FURNITURE', 'SPORTS', 'AUTOMOTIVE', 'JEWELRY',
          'BOOKS', 'HOME_APPLIANCES', 'OTHER'
        ]).notNullable().index();
        table.string('target_market', 64);
        table.json('required_certificates').notNullable();
        table.enum('compliance_level', ['REQUIRED', 'RECOMMENDED', 'OPTIONAL']).notNullable();
        table.text('description');
        table.boolean('is_active').defaultTo(true).notNullable();
        table.timestamp('created_at').defaultTo(db.fn.now());
        table.timestamp('updated_at').defaultTo(db.fn.now());

        table.unique(['tenant_id', 'platform', 'product_category', 'target_market'], 'uk_rule');
        table.index(['is_active'], 'idx_rule_active');
      });
      logger.info(`[ComplianceRuleDatabaseService] 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).index();
        table.json('backup_data').notNullable();
        table.string('version', 32).notNullable();
        table.timestamp('created_at').defaultTo(db.fn.now());
      });
      logger.info(`[ComplianceRuleDatabaseService] Backup table ${this.BACKUP_TABLE} created`);
    }
  }

  static async insert(record: ComplianceRuleDatabaseRecord): Promise<string> {
    const id = record.id || this.generateId();
    
    await db(this.TABLE_NAME).insert({
      id,
      tenant_id: record.tenantId || null,
      platform: record.platform,
      product_category: record.productCategory,
      target_market: record.targetMarket || null,
      required_certificates: JSON.stringify(record.requiredCertificates),
      compliance_level: record.complianceLevel,
      description: record.description || null,
      is_active: record.isActive,
    });

    logger.info(`[ComplianceRuleDatabaseService] Rule inserted: id=${id}, platform=${record.platform}, category=${record.productCategory}`);
    return id;
  }

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

    if (updates.platform) updateData.platform = updates.platform;
    if (updates.productCategory) updateData.product_category = updates.productCategory;
    if (updates.targetMarket !== undefined) updateData.target_market = updates.targetMarket || null;
    if (updates.requiredCertificates) updateData.required_certificates = JSON.stringify(updates.requiredCertificates);
    if (updates.complianceLevel) updateData.compliance_level = updates.complianceLevel;
    if (updates.description !== undefined) updateData.description = updates.description || null;
    if (updates.isActive !== undefined) updateData.is_active = updates.isActive;

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

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

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

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

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

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

  static async query(options: ComplianceRuleQueryOptions): Promise<ComplianceRuleDatabaseRecord[]> {
    let query = db(this.TABLE_NAME);

    if (options.tenantId !== undefined) {
      if (options.tenantId === null || options.tenantId === '') {
        query = query.whereNull('tenant_id');
      } else {
        query = query.where('tenant_id', options.tenantId);
      }
    }
    if (options.platform) {
      query = query.andWhere('platform', options.platform);
    }
    if (options.productCategory) {
      query = query.andWhere('product_category', options.productCategory);
    }
    if (options.targetMarket) {
      query = query.andWhere('target_market', options.targetMarket);
    }
    if (options.isActive !== undefined) {
      query = query.andWhere('is_active', options.isActive);
    }

    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: ComplianceRuleQueryOptions): Promise<number> {
    let query = db(this.TABLE_NAME);

    if (options.tenantId !== undefined) {
      if (options.tenantId === null || options.tenantId === '') {
        query = query.whereNull('tenant_id');
      } else {
        query = query.where('tenant_id', options.tenantId);
      }
    }
    if (options.platform) {
      query = query.andWhere('platform', options.platform);
    }
    if (options.productCategory) {
      query = query.andWhere('product_category', options.productCategory);
    }
    if (options.isActive !== undefined) {
      query = query.andWhere('is_active', options.isActive);
    }

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

  static async getRule(
    platform: PlatformType,
    productCategory: ProductCategory,
    tenantId?: string,
    targetMarket?: string
  ): Promise<ComplianceRuleDatabaseRecord | null> {
    let query = db(this.TABLE_NAME)
      .where('platform', platform)
      .where('product_category', productCategory)
      .where('is_active', true);

    if (tenantId) {
      query = query.andWhere(function() {
        this.where('tenant_id', tenantId).orWhereNull('tenant_id');
      });
    } else {
      query = query.whereNull('tenant_id');
    }

    if (targetMarket) {
      query = query.andWhere(function() {
        this.where('target_market', targetMarket).orWhereNull('target_market');
      });
    }

    const row = await query.orderBy('tenant_id', 'desc').first();
    return row ? this.mapRowToRecord(row) : null;
  }

  static async getRequiredCertificates(
    platform: PlatformType,
    productCategory: ProductCategory,
    tenantId?: string
  ): Promise<CertificateType[]> {
    const rule = await this.getRule(platform, productCategory, tenantId);
    
    if (rule) {
      return rule.requiredCertificates;
    }

    const platformCerts = this.PLATFORM_CERTIFICATE_REQUIREMENTS[platform] || [];
    const categoryCerts = this.CATEGORY_CERTIFICATE_REQUIREMENTS[productCategory] || [];
    
    const combined = new Set([...platformCerts, ...categoryCerts]);
    return Array.from(combined);
  }

  static async getStatistics(tenantId?: string): Promise<RuleStatistics> {
    let query = db(this.TABLE_NAME);
    if (tenantId) {
      query = query.where(function() {
        this.where('tenant_id', tenantId).orWhereNull('tenant_id');
      });
    }

    const records = await query;

    const stats: RuleStatistics = {
      totalRules: records.length,
      activeRules: 0,
      byPlatform: {
        AMAZON: 0,
        EBAY: 0,
        SHOPEE: 0,
        ALIEXPRESS: 0,
        TIKTOK: 0,
        TEMU: 0,
      },
      byCategory: {
        ELECTRONICS: 0,
        TOYS: 0,
        COSMETICS: 0,
        FOOD: 0,
        MEDICAL: 0,
        TEXTILES: 0,
        FURNITURE: 0,
        SPORTS: 0,
        AUTOMOTIVE: 0,
        JEWELRY: 0,
        BOOKS: 0,
        HOME_APPLIANCES: 0,
        OTHER: 0,
      },
      byComplianceLevel: {
        REQUIRED: 0,
        RECOMMENDED: 0,
        OPTIONAL: 0,
      },
      globalRules: 0,
      tenantRules: 0,
    };

    for (const record of records) {
      if (record.is_active) stats.activeRules++;
      stats.byPlatform[record.platform as PlatformType]++;
      stats.byCategory[record.product_category as ProductCategory]++;
      stats.byComplianceLevel[record.compliance_level as ComplianceLevel]++;
      
      if (record.tenant_id) {
        stats.tenantRules++;
      } else {
        stats.globalRules++;
      }
    }

    return stats;
  }

  static validateRule(rule: ComplianceRuleDatabaseRecord): RuleValidationResult {
    const result: RuleValidationResult = {
      isValid: true,
      errors: [],
      warnings: [],
    };

    if (!rule.platform) {
      result.errors.push('Platform is required');
      result.isValid = false;
    }

    if (!rule.productCategory) {
      result.errors.push('Product category is required');
      result.isValid = false;
    }

    if (!rule.requiredCertificates || rule.requiredCertificates.length === 0) {
      result.errors.push('At least one required certificate is needed');
      result.isValid = false;
    }

    if (!rule.complianceLevel) {
      result.errors.push('Compliance level is required');
      result.isValid = false;
    }

    const validCertificates: CertificateType[] = [
      'CE', 'FCC', 'ROHS', 'ISO9001', 'ISO14001',
      'FDA', 'UL', 'CCC', 'EXPORT_LICENSE', 'ORIGIN', 'OTHER'
    ];

    for (const cert of rule.requiredCertificates) {
      if (!validCertificates.includes(cert)) {
        result.errors.push(`Invalid certificate type: ${cert}`);
        result.isValid = false;
      }
    }

    if (rule.complianceLevel === 'REQUIRED' && rule.requiredCertificates.length > 5) {
      result.warnings.push('Too many required certificates may affect product listing');
    }

    return result;
  }

  static async checkConflicts(
    rule: ComplianceRuleDatabaseRecord,
    excludeId?: string
  ): Promise<RuleConflict[]> {
    const conflicts: RuleConflict[] = [];

    let query = db(this.TABLE_NAME)
      .where('platform', rule.platform)
      .where('product_category', rule.productCategory);

    if (rule.tenantId) {
      query = query.andWhere('tenant_id', rule.tenantId);
    } else {
      query = query.whereNull('tenant_id');
    }

    if (rule.targetMarket) {
      query = query.andWhere('target_market', rule.targetMarket);
    } else {
      query = query.whereNull('target_market');
    }

    if (excludeId) {
      query = query.andWhereNot('id', excludeId);
    }

    const existing = await query;

    for (const existingRule of existing) {
      const existingRecord = this.mapRowToRecord(existingRule);
      
      conflicts.push({
        existingRule: existingRecord,
        newRule: rule,
        conflictType: 'PLATFORM_CATEGORY',
      });
    }

    return conflicts;
  }

  static async backup(tenantId: string | undefined, traceId: string): Promise<string> {
    let query = db(this.TABLE_NAME);
    if (tenantId) {
      query = query.where(function() {
        this.where('tenant_id', tenantId).orWhereNull('tenant_id');
      });
    }

    const records = await query;
    
    const backupData: RuleBackupData = {
      version: this.VERSION,
      exportedAt: new Date(),
      tenantId,
      totalRecords: records.length,
      rules: records.map(this.mapRowToRecord),
    };

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

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

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

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

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

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

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

      result.success = result.errors.length === 0;
      logger.info(`[ComplianceRuleDatabaseService] 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<RuleBackupData> {
    let query = db(this.TABLE_NAME);
    if (tenantId) {
      query = query.where(function() {
        this.where('tenant_id', tenantId).orWhereNull('tenant_id');
      });
    }

    const records = await query;
    
    const exportData: RuleBackupData = {
      version: this.VERSION,
      exportedAt: new Date(),
      tenantId,
      totalRecords: records.length,
      rules: records.map(this.mapRowToRecord),
    };

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

  static async bulkImport(
    rules: ComplianceRuleDatabaseRecord[],
    traceId: string,
    skipConflicts: boolean = true
  ): Promise<RuleImportResult> {
    const result: RuleImportResult = {
      success: 0,
      failed: 0,
      errors: [],
    };

    for (let i = 0; i < rules.length; i++) {
      const rule = rules[i];
      try {
        const validation = this.validateRule(rule);
        if (!validation.isValid) {
          throw new Error(validation.errors.join('; '));
        }

        const conflicts = await this.checkConflicts(rule);
        if (conflicts.length > 0 && !skipConflicts) {
          throw new Error(`Rule conflicts with existing rule: ${conflicts[0].existingRule.id}`);
        }

        if (conflicts.length > 0 && skipConflicts) {
          continue;
        }

        await this.insert(rule);
        result.success++;
      } catch (error: any) {
        result.failed++;
        result.errors.push({ row: i + 1, error: error.message });
      }
    }

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

  static async initDefaultRules(traceId: string): Promise<number> {
    const defaultRules: ComplianceRuleDatabaseRecord[] = [
      {
        id: 'rule_electronics_amazon',
        platform: 'AMAZON',
        productCategory: 'ELECTRONICS',
        requiredCertificates: ['CE', 'FCC', 'ROHS'],
        complianceLevel: 'REQUIRED',
        description: 'Amazon electronics products require CE, FCC, and ROHS certifications',
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        id: 'rule_toys_amazon',
        platform: 'AMAZON',
        productCategory: 'TOYS',
        requiredCertificates: ['CE', 'EN71'],
        complianceLevel: 'REQUIRED',
        description: 'Amazon toys require CE and EN71 certifications',
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        id: 'rule_cosmetics_amazon',
        platform: 'AMAZON',
        productCategory: 'COSMETICS',
        requiredCertificates: ['FDA'],
        complianceLevel: 'REQUIRED',
        description: 'Amazon cosmetics require FDA certification',
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        id: 'rule_medical_amazon',
        platform: 'AMAZON',
        productCategory: 'MEDICAL',
        requiredCertificates: ['FDA', 'CE', 'ISO13485'],
        complianceLevel: 'REQUIRED',
        description: 'Amazon medical devices require FDA, CE, and ISO13485 certifications',
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        id: 'rule_electronics_shopee',
        platform: 'SHOPEE',
        productCategory: 'ELECTRONICS',
        requiredCertificates: ['CE', 'ROHS'],
        complianceLevel: 'REQUIRED',
        description: 'Shopee electronics require CE and ROHS certifications',
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
      {
        id: 'rule_food_amazon',
        platform: 'AMAZON',
        productCategory: 'FOOD',
        requiredCertificates: ['FDA', 'HACCP'],
        complianceLevel: 'REQUIRED',
        description: 'Amazon food products require FDA and HACCP certifications',
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ];

    let inserted = 0;
    for (const rule of defaultRules) {
      try {
        const existing = await this.getById(rule.id);
        if (!existing) {
          await this.insert(rule);
          inserted++;
        }
      } catch (error: any) {
        logger.error(`[ComplianceRuleDatabaseService] Failed to insert default rule: ${rule.id}, error=${error.message}`);
      }
    }

    logger.info(`[ComplianceRuleDatabaseService] Initialized ${inserted} default rules, traceId=${traceId}`);
    return inserted;
  }

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

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

  private static mapRowToRecord(row: Record<string, any>): ComplianceRuleDatabaseRecord {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      platform: row.platform,
      productCategory: row.product_category,
      targetMarket: row.target_market,
      requiredCertificates: JSON.parse(row.required_certificates),
      complianceLevel: row.compliance_level,
      description: row.description,
      isActive: Boolean(row.is_active),
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }
}
