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

export interface TaskCenterTask {
  id: string;
  tenantId: string;
  shopId: string;
  taskType: string;
  taskName: string;
  status: 'PENDING' | 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
  priority: 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT';
  progress: number;
  input: Record<string, any>;
  output?: Record<string, any>;
  error?: string;
  assignedTo?: string;
  startedAt?: string;
  completedAt?: string;
  createdAt: string;
  updatedAt: string;
  traceId: string;
}

export interface TaskStats {
  total: number;
  pending: number;
  running: number;
  completed: number;
  failed: number;
}

export class TaskCenterService {
  private static TABLE = 'cf_task_center';

  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('shop_id', 64);
        table.string('task_type', 64).notNullable();
        table.string('task_name', 256).notNullable();
        table.enum('status', ['PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED']).defaultTo('PENDING');
        table.enum('priority', ['LOW', 'MEDIUM', 'HIGH', 'URGENT']).defaultTo('MEDIUM');
        table.integer('progress').defaultTo(0);
        table.json('input');
        table.json('output');
        table.text('error');
        table.string('assigned_to', 64);
        table.timestamp('started_at');
        table.timestamp('completed_at');
        table.string('trace_id', 128);
        table.timestamps(true, true);
        table.index(['tenant_id', 'status']);
        table.index(['shop_id', 'status']);
        table.index(['task_type']);
      });
    }
  }

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

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

  static async create(tenantId: string, data: {
    shopId?: string;
    taskType: string;
    taskName: string;
    priority?: 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT';
    input?: Record<string, any>;
    assignedTo?: string;
    traceId: string;
  }): Promise<TaskCenterTask> {
    const id = this.generateId();
    const now = new Date().toISOString();
    const task: TaskCenterTask = {
      id,
      tenantId,
      shopId: data.shopId || '',
      taskType: data.taskType,
      taskName: data.taskName,
      status: 'PENDING',
      priority: data.priority || 'MEDIUM',
      progress: 0,
      input: data.input || {},
      assignedTo: data.assignedTo,
      createdAt: now,
      updatedAt: now,
      traceId: data.traceId,
    };
    await db(this.TABLE).insert({
      id: task.id,
      tenant_id: task.tenantId,
      shop_id: task.shopId,
      task_type: task.taskType,
      task_name: task.taskName,
      status: task.status,
      priority: task.priority,
      progress: task.progress,
      input: JSON.stringify(task.input),
      assigned_to: task.assignedTo,
      trace_id: task.traceId,
      created_at: task.createdAt,
      updated_at: task.updatedAt,
    });
    await DomainEventBus.publish('task_center.created', { taskId: id, tenantId, traceId: data.traceId });
    return task;
  }

  static async updateStatus(id: string, status: TaskCenterTask['status'], progress?: number, output?: Record<string, any>, error?: string): Promise<TaskCenterTask> {
    const updateData: Record<string, any> = {
      status,
      updated_at: new Date().toISOString(),
    };
    if (progress !== undefined) updateData.progress = progress;
    if (output) updateData.output = JSON.stringify(output);
    if (error) updateData.error = error;
    if (status === 'RUNNING') updateData.started_at = new Date().toISOString();
    if (status === 'COMPLETED' || status === 'FAILED') updateData.completed_at = new Date().toISOString();
    await db(this.TABLE).where({ id }).update(updateData);
    const task = await this.getById(id);
    await DomainEventBus.publish('task_center.status_updated', { taskId: id, status });
    return task!;
  }

  static async cancel(id: string): Promise<void> {
    await db(this.TABLE).where({ id }).update({
      status: 'CANCELLED',
      completed_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    });
    await DomainEventBus.publish('task_center.cancelled', { taskId: id });
  }

  static async retry(id: string): Promise<TaskCenterTask> {
    const task = await this.getById(id);
    if (!task) throw new Error('Task not found');
    const newTask = await this.create(task.tenantId, {
      shopId: task.shopId,
      taskType: task.taskType,
      taskName: task.taskName,
      priority: task.priority,
      input: task.input,
      traceId: `${task.traceId}_retry_${Date.now()}`,
    });
    return newTask;
  }

  static async getStats(tenantId: string): Promise<TaskStats> {
    const tasks = await this.list(tenantId);
    return {
      total: tasks.length,
      pending: tasks.filter((t) => t.status === 'PENDING').length,
      running: tasks.filter((t) => t.status === 'RUNNING').length,
      completed: tasks.filter((t) => t.status === 'COMPLETED').length,
      failed: tasks.filter((t) => t.status === 'FAILED').length,
    };
  }

  static async getPendingTasks(tenantId: string, limit: number = 10): Promise<TaskCenterTask[]> {
    return db(this.TABLE)
      .where('tenant_id', tenantId)
      .where('status', 'PENDING')
      .orderBy('priority', 'desc')
      .orderBy('created_at', 'asc')
      .limit(limit);
  }

  static async assignTask(id: string, assignedTo: string): Promise<void> {
    await db(this.TABLE).where({ id }).update({
      assigned_to: assignedTo,
      updated_at: new Date().toISOString(),
    });
  }

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