import db from '../../config/database';
import { DomainEventBus } from '../../core/runtime/DomainEventBus';
import { logger } from '../../utils/logger';

export interface WinNode {
  id: string;
  tenantId: string;
  name: string;
  host: string;
  port: number;
  status: 'ONLINE' | 'OFFLINE' | 'BUSY' | 'ERROR';
  shopId: string;
  shopName: string;
  profileDir: string;
  proxy: string;
  fingerprintPolicy: 'STANDARD' | 'STEALTH' | 'RANDOM' | 'CUSTOM';
  maxConcurrent: number;
  currentTasks: number;
  cpuUsage: number;
  memoryUsage: number;
  lastHeartbeat: string;
  autoRestart: boolean;
  createdAt: string;
  updatedAt: string;
}

export interface WinNodeHeartbeat {
  nodeId: string;
  cpuUsage: number;
  memoryUsage: number;
  currentTasks: number;
  timestamp: string;
}

export class WinNodeService {
  private static TABLE = 'cf_win_node';

  static async initTable(): Promise<void> {
    const hasTable = await db.schema.hasTable(this.TABLE);
    if (!hasTable) {
      await db.schema.createTable(this.TABLE, (table) => {
        table.string('id', 64).primary();
        table.string('tenant_id', 64).notNullable();
        table.string('name', 128).notNullable();
        table.string('host', 64).notNullable();
        table.integer('port').notNullable();
        table.enum('status', ['ONLINE', 'OFFLINE', 'BUSY', 'ERROR']).defaultTo('OFFLINE');
        table.string('shop_id', 64);
        table.string('shop_name', 128);
        table.string('profile_dir', 256).notNullable();
        table.string('proxy', 256);
        table.enum('fingerprint_policy', ['STANDARD', 'STEALTH', 'RANDOM', 'CUSTOM']).defaultTo('STANDARD');
        table.integer('max_concurrent').defaultTo(3);
        table.integer('current_tasks').defaultTo(0);
        table.decimal('cpu_usage', 5, 2).defaultTo(0);
        table.decimal('memory_usage', 5, 2).defaultTo(0);
        table.timestamp('last_heartbeat');
        table.boolean('auto_restart').defaultTo(true);
        table.timestamps(true, true);
        table.index(['tenant_id', 'status']);
        table.index(['shop_id']);
      });
    }
  }

  static async list(tenantId: string, filters?: { status?: string; shopId?: string }): Promise<WinNode[]> {
    let query = db(this.TABLE).where('tenant_id', tenantId);
    if (filters?.status) query = query.where('status', filters.status);
    if (filters?.shopId) query = query.where('shop_id', filters.shopId);
    return query.orderBy('created_at', 'desc');
  }

  static async getById(id: string): Promise<WinNode | null> {
    const node = await db(this.TABLE).where({ id }).first();
    return node || null;
  }

  static async create(tenantId: string, data: Omit<WinNode, 'id' | 'tenantId' | 'createdAt' | 'updatedAt' | 'currentTasks' | 'cpuUsage' | 'memoryUsage' | 'lastHeartbeat'>): Promise<WinNode> {
    const id = this.generateId();
    const now = new Date().toISOString();
    const node: WinNode = {
      id,
      tenantId,
      ...data,
      currentTasks: 0,
      cpuUsage: 0,
      memoryUsage: 0,
      lastHeartbeat: now,
      createdAt: now,
      updatedAt: now,
    };
    await db(this.TABLE).insert({
      id: node.id,
      tenant_id: node.tenantId,
      name: node.name,
      host: node.host,
      port: node.port,
      status: node.status,
      shop_id: node.shopId,
      shop_name: node.shopName,
      profile_dir: node.profileDir,
      proxy: node.proxy,
      fingerprint_policy: node.fingerprintPolicy,
      max_concurrent: node.maxConcurrent,
      current_tasks: node.currentTasks,
      cpu_usage: node.cpuUsage,
      memory_usage: node.memoryUsage,
      last_heartbeat: node.lastHeartbeat,
      auto_restart: node.autoRestart,
      created_at: node.createdAt,
      updated_at: node.updatedAt,
    });
    await DomainEventBus.publish('win_node.created', { nodeId: id, tenantId });
    return node;
  }

  static async update(id: string, data: Partial<WinNode>): Promise<WinNode> {
    const updateData: Record<string, any> = { updated_at: new Date().toISOString() };
    if (data.name) updateData.name = data.name;
    if (data.host) updateData.host = data.host;
    if (data.port) updateData.port = data.port;
    if (data.status) updateData.status = data.status;
    if (data.shopId !== undefined) updateData.shop_id = data.shopId;
    if (data.shopName) updateData.shop_name = data.shopName;
    if (data.profileDir) updateData.profile_dir = data.profileDir;
    if (data.proxy) updateData.proxy = data.proxy;
    if (data.fingerprintPolicy) updateData.fingerprint_policy = data.fingerprintPolicy;
    if (data.maxConcurrent !== undefined) updateData.max_concurrent = data.maxConcurrent;
    if (data.autoRestart !== undefined) updateData.auto_restart = data.autoRestart;
    await db(this.TABLE).where({ id }).update(updateData);
    const node = await this.getById(id);
    await DomainEventBus.publish('win_node.updated', { nodeId: id });
    return node!;
  }

  static async delete(id: string): Promise<void> {
    await db(this.TABLE).where({ id }).delete();
    await DomainEventBus.publish('win_node.deleted', { nodeId: id });
  }

  static async heartbeat(nodeId: string, data: WinNodeHeartbeat): Promise<void> {
    await db(this.TABLE).where({ id: nodeId }).update({
      cpu_usage: data.cpuUsage,
      memory_usage: data.memoryUsage,
      current_tasks: data.currentTasks,
      last_heartbeat: data.timestamp,
      status: data.currentTasks > 0 ? 'BUSY' : 'ONLINE',
      updated_at: new Date().toISOString(),
    });
  }

  static async testConnection(id: string): Promise<{ success: boolean; message: string }> {
    const node = await this.getById(id);
    if (!node) return { success: false, message: 'Node not found' };
    try {
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), 5000);
      const response = await fetch(`http://${node.host}:${node.port}/json/version`, {
        signal: controller.signal,
      });
      clearTimeout(timeoutId);
      if (response.ok) {
        await db(this.TABLE).where({ id }).update({ status: 'ONLINE', updated_at: new Date().toISOString() });
        return { success: true, message: 'Connection successful' };
      }
      return { success: false, message: `HTTP ${response.status}` };
    } catch (error: any) {
      await db(this.TABLE).where({ id }).update({ status: 'ERROR', updated_at: new Date().toISOString() });
      return { success: false, message: error.message };
    }
  }

  static async restart(id: string): Promise<{ success: boolean; message: string }> {
    const node = await this.getById(id);
    if (!node) return { success: false, message: 'Node not found' };
    await DomainEventBus.publish('win_node.restart_requested', { nodeId: id, shopId: node.shopId });
    return { success: true, message: 'Restart command sent' };
  }

  static async getStats(tenantId: string): Promise<{ total: number; online: number; busy: number; offline: number }> {
    const nodes = await this.list(tenantId);
    return {
      total: nodes.length,
      online: nodes.filter((n) => n.status === 'ONLINE').length,
      busy: nodes.filter((n) => n.status === 'BUSY').length,
      offline: nodes.filter((n) => n.status === 'OFFLINE' || n.status === 'ERROR').length,
    };
  }

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