import { v4 as uuidv4 } from 'uuid';
import db from '../config/database';
import { logger } from '../utils/logger';
import { AutopilotSessionStatus, AutopilotMode, LogType, TaskStatus } from '../types/enums';

export interface AutopilotSession {
  id: string;
  tenant_id: string;
  shop_id: string;
  status: AutopilotSessionStatus;
  mode: AutopilotMode;
  config: Record<string, any>;
  enabled_features: string[];
  total_revenue: number;
  total_orders: number;
  total_profit: number;
  tasks_completed: number;
  tasks_failed: number;
  started_at: Date | null;
  stopped_at: Date | null;
  last_activity_at: Date | null;
  error_message: string | null;
  created_at: Date;
  updated_at: Date;
}

export interface AutopilotLog {
  id: string;
  session_id: string;
  tenant_id: string;
  log_type: LogType;
  task_type: string | null;
  task_status: TaskStatus;
  description: string;
  input_data: Record<string, any> | null;
  output_data: Record<string, any> | null;
  error_details: Record<string, any> | null;
  duration_ms: number;
  is_auto_executed: boolean;
  trace_id: string | null;
  created_at: Date;
}

export interface AutopilotConfig {
  pricing: {
    enabled: boolean;
    min_margin: number;
    max_adjustment: number;
    competitor_monitoring: boolean;
  };
  inventory: {
    enabled: boolean;
    reorder_threshold: number;
    auto_reorder: boolean;
  };
  advertising: {
    enabled: boolean;
    budget_limit: number;
    target_acos: number;
    auto_adjust_bids: boolean;
  };
}

export interface SessionCreationInput {
  tenantId: string;
  shopId: string;
  mode: AutopilotMode;
  config: AutopilotConfig;
}

export interface TaskExecutionInput {
  sessionId: string;
  taskType: string;
  input: Record<string, any>;
  isAutoExecuted?: boolean;
}

export class AutoPilotService {
  private static instance: AutoPilotService;

  private constructor() {}

  static getInstance(): AutoPilotService {
    if (!AutoPilotService.instance) {
      AutoPilotService.instance = new AutoPilotService();
    }
    return AutoPilotService.instance;
  }

  async createSession(input: SessionCreationInput): Promise<AutopilotSession> {
    const { tenantId, shopId, mode, config } = input;

    const id = uuidv4();
    const now = new Date();

    await db('cf_autopilot_sessions').insert({
      id,
      tenant_id: tenantId,
      shop_id: shopId,
      status: AutopilotSessionStatus.IDLE,
      mode,
      config: JSON.stringify(config),
      enabled_features: JSON.stringify(this.extractEnabledFeatures(config)),
      total_revenue: 0,
      total_orders: 0,
      total_profit: 0,
      tasks_completed: 0,
      tasks_failed: 0,
      started_at: null,
      stopped_at: null,
      last_activity_at: null,
      error_message: null,
      created_at: now,
      updated_at: now
    });

    const session = await this.getSessionById(id);
    if (!session) {
      throw new Error('Failed to create session');
    }

    logger.info(`[AutoPilotService] Created session ${id} for tenant ${tenantId}`);
    return session;
  }

  async startSession(sessionId: string): Promise<AutopilotSession> {
    const session = await this.getSessionById(sessionId);
    if (!session) {
      throw new Error('Session not found');
    }

    if (session.status !== AutopilotSessionStatus.IDLE && session.status !== AutopilotSessionStatus.PAUSED) {
      throw new Error(`Cannot start session in ${session.status} status`);
    }

    await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .update({
        status: AutopilotSessionStatus.RUNNING,
        started_at: new Date(),
        last_activity_at: new Date(),
        updated_at: new Date()
      });

    await this.logActivity({
      session_id: sessionId,
      tenant_id: session.tenant_id,
      log_type: LogType.INFO,
      task_type: null,
      task_status: TaskStatus.COMPLETED,
      description: `Autopilot session started in ${session.mode} mode`,
      input_data: null,
      output_data: null,
      error_details: null,
      duration_ms: 0,
      is_auto_executed: false,
      trace_id: null
    });

    logger.info(`[AutoPilotService] Started session ${sessionId}`);

    const updatedSession = await this.getSessionById(sessionId);
    if (!updatedSession) {
      throw new Error('Failed to get updated session');
    }
    return updatedSession;
  }

  async pauseSession(sessionId: string): Promise<void> {
    const session = await this.getSessionById(sessionId);
    if (!session) {
      throw new Error('Session not found');
    }

    if (session.status !== AutopilotSessionStatus.RUNNING) {
      throw new Error(`Cannot pause session in ${session.status} status`);
    }

    await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .update({
        status: AutopilotSessionStatus.PAUSED,
        last_activity_at: new Date(),
        updated_at: new Date()
      });

    await this.logActivity({
      session_id: sessionId,
      tenant_id: session.tenant_id,
      log_type: LogType.INFO,
      task_type: null,
      task_status: TaskStatus.COMPLETED,
      description: 'Autopilot session paused',
      input_data: null,
      output_data: null,
      error_details: null,
      duration_ms: 0,
      is_auto_executed: false,
      trace_id: null
    });

    logger.info(`[AutoPilotService] Paused session ${sessionId}`);
  }

  async resumeSession(sessionId: string): Promise<void> {
    const session = await this.getSessionById(sessionId);
    if (!session) {
      throw new Error('Session not found');
    }

    if (session.status !== AutopilotSessionStatus.PAUSED) {
      throw new Error(`Cannot resume session in ${session.status} status`);
    }

    await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .update({
        status: AutopilotSessionStatus.RUNNING,
        last_activity_at: new Date(),
        updated_at: new Date()
      });

    await this.logActivity({
      session_id: sessionId,
      tenant_id: session.tenant_id,
      log_type: LogType.INFO,
      task_type: null,
      task_status: TaskStatus.COMPLETED,
      description: 'Autopilot session resumed',
      input_data: null,
      output_data: null,
      error_details: null,
      duration_ms: 0,
      is_auto_executed: false,
      trace_id: null
    });

    logger.info(`[AutoPilotService] Resumed session ${sessionId}`);
  }

  async stopSession(sessionId: string, tenantId: string): Promise<AutopilotSession> {
    const session = await this.getSessionById(sessionId);
    if (!session) {
      throw new Error('Session not found');
    }

    if (session.tenant_id !== tenantId) {
      throw new Error('Unauthorized');
    }

    await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .update({
        status: AutopilotSessionStatus.STOPPED,
        stopped_at: new Date(),
        last_activity_at: new Date(),
        updated_at: new Date()
      });

    await this.logActivity({
      session_id: sessionId,
      tenant_id: session.tenant_id,
      log_type: LogType.INFO,
      task_type: null,
      task_status: TaskStatus.COMPLETED,
      description: 'Autopilot session stopped',
      input_data: null,
      output_data: null,
      error_details: null,
      duration_ms: 0,
      is_auto_executed: false,
      trace_id: null
    });

    logger.info(`[AutoPilotService] Stopped session ${sessionId}`);

    const updatedSession = await this.getSessionById(sessionId);
    if (!updatedSession) {
      throw new Error('Failed to get updated session');
    }
    return updatedSession;
  }

  async emergencyStop(sessionId: string, reason: string): Promise<void> {
    const session = await this.getSessionById(sessionId);
    if (!session) {
      throw new Error('Session not found');
    }

    await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .update({
        status: AutopilotSessionStatus.ERROR,
        stopped_at: new Date(),
        error_message: `EMERGENCY STOP: ${reason}`,
        updated_at: new Date()
      });

    await this.logActivity({
      session_id: sessionId,
      tenant_id: session.tenant_id,
      log_type: LogType.ERROR,
      task_type: null,
      task_status: TaskStatus.FAILED,
      description: `Emergency stop triggered: ${reason}`,
      input_data: null,
      output_data: null,
      error_details: { reason, emergency: true },
      duration_ms: 0,
      is_auto_executed: false,
      trace_id: null
    });

    logger.error(`[AutoPilotService] Emergency stop for session ${sessionId}: ${reason}`);
  }

  async getSessionById(sessionId: string): Promise<AutopilotSession | null> {
    const session = await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .first();

    if (!session) return null;

    return this.mapSessionFromDb(session);
  }

  async getActiveSession(tenantId: string, shopId: string): Promise<AutopilotSession | null> {
    const session = await db('cf_autopilot_sessions')
      .where('tenant_id', tenantId)
      .where('shop_id', shopId)
      .whereIn('status', [AutopilotSessionStatus.IDLE, AutopilotSessionStatus.RUNNING, AutopilotSessionStatus.PAUSED])
      .first();

    if (!session) return null;

    return this.mapSessionFromDb(session);
  }

  async getSessionsByTenant(tenantId: string): Promise<AutopilotSession[]> {
    const sessions = await db('cf_autopilot_sessions')
      .where('tenant_id', tenantId)
      .orderBy('created_at', 'desc');

    return sessions.map(this.mapSessionFromDb);
  }

  async updateSessionStats(
    sessionId: string,
    stats: {
      revenue?: number;
      orders?: number;
      profit?: number;
      tasksCompleted?: number;
      tasksFailed?: number;
    }
  ): Promise<void> {
    const updateData: any = {
      updated_at: new Date(),
      last_activity_at: new Date()
    };

    if (stats.revenue !== undefined) {
      updateData.total_revenue = db.raw('total_revenue + ?', [stats.revenue]);
    }
    if (stats.orders !== undefined) {
      updateData.total_orders = db.raw('total_orders + ?', [stats.orders]);
    }
    if (stats.profit !== undefined) {
      updateData.total_profit = db.raw('total_profit + ?', [stats.profit]);
    }
    if (stats.tasksCompleted !== undefined) {
      updateData.tasks_completed = db.raw('tasks_completed + ?', [stats.tasksCompleted]);
    }
    if (stats.tasksFailed !== undefined) {
      updateData.tasks_failed = db.raw('tasks_failed + ?', [stats.tasksFailed]);
    }

    await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .update(updateData);
  }

  async updateSessionConfig(sessionId: string, config: Partial<AutopilotConfig>): Promise<void> {
    const session = await this.getSessionById(sessionId);
    if (!session) {
      throw new Error('Session not found');
    }

    const currentConfig = session.config;
    const newConfig = { ...currentConfig, ...config };

    await db('cf_autopilot_sessions')
      .where('id', sessionId)
      .update({
        config: JSON.stringify(newConfig),
        enabled_features: JSON.stringify(this.extractEnabledFeatures(newConfig)),
        updated_at: new Date()
      });

    logger.info(`[AutoPilotService] Updated config for session ${sessionId}`);
  }

  async logActivity(log: Omit<AutopilotLog, 'id' | 'created_at'>): Promise<AutopilotLog> {
    const newLog: AutopilotLog = {
      ...log,
      id: uuidv4(),
      created_at: new Date()
    };

    await db('cf_autopilot_logs').insert({
      ...newLog,
      input_data: newLog.input_data ? JSON.stringify(newLog.input_data) : null,
      output_data: newLog.output_data ? JSON.stringify(newLog.output_data) : null,
      error_details: newLog.error_details ? JSON.stringify(newLog.error_details) : null
    });

    return newLog;
  }

  async getSessionLogs(
    sessionId: string,
    options?: {
      logType?: string;
      taskType?: string;
      limit?: number;
      offset?: number;
    }
  ): Promise<{ logs: AutopilotLog[]; total: number }> {
    let query = db('cf_autopilot_logs')
      .where('session_id', sessionId);

    if (options?.logType) {
      query = query.where('log_type', options.logType);
    }
    if (options?.taskType) {
      query = query.where('task_type', options.taskType);
    }

    const total = await query.clone().count('* as count').first();

    if (options?.limit) {
      query = query.limit(options.limit);
    }
    if (options?.offset) {
      query = query.offset(options.offset);
    }

    const logs = await query.orderBy('created_at', 'desc');

    return {
      logs: logs.map(this.mapLogFromDb),
      total: Number(total?.count) || 0
    };
  }

  async getRecentLogs(tenantId: string, limit: number = 50): Promise<AutopilotLog[]> {
    const logs = await db('cf_autopilot_logs')
      .where('tenant_id', tenantId)
      .orderBy('created_at', 'desc')
      .limit(limit);

    return logs.map(this.mapLogFromDb);
  }

  async executeTask(input: TaskExecutionInput): Promise<{ success: boolean; result?: any; error?: string }> {
    const { sessionId, taskType, input: taskInput, isAutoExecuted = false } = input;

    const session = await this.getSessionById(sessionId);
    if (!session) {
      throw new Error('Session not found');
    }

    if (session.status !== AutopilotSessionStatus.RUNNING) {
      throw new Error('Session is not running');
    }

    const startTime = Date.now();
    let taskStatus: TaskStatus = TaskStatus.PENDING;
    let result: any = null;
    let error: string | null = null;

    try {
      // Execute task based on type
      switch (taskType) {
        case 'PRICE_ADJUSTMENT':
          result = await this.executePriceAdjustment(session, taskInput);
          break;
        case 'INVENTORY_CHECK':
          result = await this.executeInventoryCheck(session, taskInput);
          break;
        case 'AD_OPTIMIZATION':
          result = await this.executeAdOptimization(session, taskInput);
          break;
        default:
          throw new Error(`Unknown task type: ${taskType}`);
      }

      taskStatus = TaskStatus.COMPLETED;
      await this.updateSessionStats(sessionId, { tasksCompleted: 1 });
    } catch (err: any) {
      taskStatus = TaskStatus.FAILED;
      error = err.message;
      await this.updateSessionStats(sessionId, { tasksFailed: 1 });
    }

    const duration = Date.now() - startTime;

    await this.logActivity({
      session_id: sessionId,
      tenant_id: session.tenant_id,
      log_type: taskStatus === TaskStatus.COMPLETED ? LogType.TASK : LogType.ERROR,
      task_type: taskType,
      task_status: taskStatus,
      description: `${taskType} ${taskStatus.toLowerCase()}`,
      input_data: taskInput,
      output_data: result,
      error_details: error ? { message: error } : null,
      duration_ms: duration,
      is_auto_executed: isAutoExecuted,
      trace_id: null
    });

    return {
      success: taskStatus === TaskStatus.COMPLETED,
      result,
      error: error || undefined
    };
  }

  private async executePriceAdjustment(session: AutopilotSession, input: any): Promise<any> {
    // Implementation for price adjustment
    logger.info(`[AutoPilotService] Executing price adjustment for session ${session.id}`);
    return { adjusted: true, products: [] };
  }

  private async executeInventoryCheck(session: AutopilotSession, input: any): Promise<any> {
    // Implementation for inventory check
    logger.info(`[AutoPilotService] Executing inventory check for session ${session.id}`);
    return { checked: true, alerts: [] };
  }

  private async executeAdOptimization(session: AutopilotSession, input: any): Promise<any> {
    // Implementation for ad optimization
    logger.info(`[AutoPilotService] Executing ad optimization for session ${session.id}`);
    return { optimized: true, campaigns: [] };
  }

  private extractEnabledFeatures(config: Partial<AutopilotConfig>): string[] {
    const features: string[] = [];
    if (config.pricing?.enabled) features.push('pricing');
    if (config.inventory?.enabled) features.push('inventory');
    if (config.advertising?.enabled) features.push('advertising');
    return features;
  }

  private mapSessionFromDb(row: any): AutopilotSession {
    return {
      id: row.id,
      tenant_id: row.tenant_id,
      shop_id: row.shop_id,
      status: row.status as AutopilotSessionStatus,
      mode: row.mode as AutopilotMode,
      config: typeof row.config === 'string' ? JSON.parse(row.config) : row.config,
      enabled_features: typeof row.enabled_features === 'string' ? JSON.parse(row.enabled_features) : row.enabled_features,
      total_revenue: row.total_revenue,
      total_orders: row.total_orders,
      total_profit: row.total_profit,
      tasks_completed: row.tasks_completed,
      tasks_failed: row.tasks_failed,
      started_at: row.started_at ? new Date(row.started_at) : null,
      stopped_at: row.stopped_at ? new Date(row.stopped_at) : null,
      last_activity_at: row.last_activity_at ? new Date(row.last_activity_at) : null,
      error_message: row.error_message,
      created_at: new Date(row.created_at),
      updated_at: new Date(row.updated_at)
    };
  }

  private mapLogFromDb(row: any): AutopilotLog {
    return {
      id: row.id,
      session_id: row.session_id,
      tenant_id: row.tenant_id,
      log_type: row.log_type as LogType,
      task_type: row.task_type,
      task_status: row.task_status as TaskStatus,
      description: row.description,
      input_data: row.input_data ? JSON.parse(row.input_data) : null,
      output_data: row.output_data ? JSON.parse(row.output_data) : null,
      error_details: row.error_details ? JSON.parse(row.error_details) : null,
      duration_ms: row.duration_ms,
      is_auto_executed: row.is_auto_executed,
      trace_id: row.trace_id,
      created_at: new Date(row.created_at)
    };
  }
}

export default AutoPilotService.getInstance();
