import { Controller, Get, Post, Put, Patch, Param, Body, NotFoundException, HttpException, HttpStatus } from '@nestjs/common';
import { StandingOrderRepository } from '../../../infrastructure/repositories/standing-order.repository';
import { StandingOrderExecutionRepository } from '../../../infrastructure/repositories/standing-order-execution.repository';
import { StandingOrder } from '../../../domain/entities/standing-order.entity';
import { TenantContextService } from '@it0/common';
import * as crypto from 'crypto';

@Controller('api/v1/ops/standing-orders')
export class StandingOrderController {
  constructor(
    private readonly standingOrderRepository: StandingOrderRepository,
    private readonly executionRepository: StandingOrderExecutionRepository,
  ) {}

  @Get()
  async listOrders() {
    const orders = await this.standingOrderRepository.findAll();
    return orders.map((o) => this.serializeOrder(o));
  }

  /**
   * Maps incoming body from either web-admin simplified format or internal API format
   * to the StandingOrder entity fields.
   */
  private mapBodyToOrder(order: StandingOrder, body: any): void {
    if (body.name !== undefined) order.name = body.name;
    if (body.description !== undefined) order.description = body.description;
    if (body.definedInSessionId !== undefined) order.definedInSessionId = body.definedInSessionId;
    if (body.escalationPolicyId !== undefined) order.escalationPolicyId = body.escalationPolicyId;

    // Trigger: accept both { trigger: { type, ... } } and { triggerConfig: { triggerType, ... } }
    if (body.trigger) {
      order.triggerType = body.trigger.type ?? 'manual';
      order.trigger = body.trigger;
    } else if (body.triggerConfig) {
      const tc = body.triggerConfig;
      order.triggerType = tc.triggerType ?? 'manual';
      order.trigger = {
        type: tc.triggerType ?? 'manual',
        cronExpression: tc.cronExpression,
        eventType: tc.eventType,
        thresholdCondition: tc.metric
          ? { metricType: tc.metric, operator: '>=', value: tc.threshold ?? 0, durationSeconds: 60 }
          : undefined,
      };
    }

    // Targets: accept both { targets: { serverIds } } and { targetServers: string[] }
    if (body.targets) {
      order.targets = body.targets;
    } else if (body.targetServers) {
      order.targets = { serverIds: body.targetServers };
    }

    // Agent instructions: accept nested, flat, or simplified format
    const ai = body.agentInstructions;
    order.agentPrompt = ai?.prompt ?? body.agentPrompt ?? body.agentInstruction ?? order.agentPrompt ?? '';
    order.agentSkills = ai?.skills ?? body.agentSkills ?? order.agentSkills;
    order.runbookId = ai?.runbookId ?? body.runbookId ?? order.runbookId;
    order.maxRiskLevel = ai?.maxRiskLevel ?? body.maxRiskLevel ?? order.maxRiskLevel ?? 0;
    order.maxTurns = ai?.maxTurns ?? body.maxTurns ?? order.maxTurns ?? 20;
    order.maxBudgetUsd = ai?.maxBudgetUsd ?? body.maxBudgetUsd ?? body.maxBudget ?? order.maxBudgetUsd;

    // Decision boundary: accept full or simplified
    if (body.decisionBoundary) {
      order.decisionBoundary = body.decisionBoundary;
    } else if (body.escalateOnFailure !== undefined) {
      order.decisionBoundary = order.decisionBoundary ?? { allowedActions: [], escalateConditions: [], escalationRules: [] };
      order.decisionBoundary.escalateConditions = body.escalateOnFailure ? ['on_failure'] : [];
    }
  }

  /**
   * Returns a serialized order with additional web-admin friendly aliases.
   */
  private serializeOrder(order: StandingOrder) {
    return {
      ...order,
      // Web admin aliases
      triggerConfig: {
        triggerType: order.trigger?.type ?? order.triggerType,
        cronExpression: order.trigger?.cronExpression,
        eventType: order.trigger?.eventType,
        metric: order.trigger?.thresholdCondition?.metricType,
        threshold: order.trigger?.thresholdCondition?.value,
      },
      targetServers: order.targets?.serverIds ?? [],
      agentInstruction: order.agentPrompt,
      maxBudget: order.maxBudgetUsd ?? 0,
      escalateOnFailure: order.decisionBoundary?.escalateConditions?.includes('on_failure') ?? false,
    };
  }

  @Post()
  async createOrder(@Body() body: any) {
    // Quota enforcement
    const tenant = TenantContextService.getTenant();
    if (tenant && tenant.maxStandingOrders !== -1) {
      const existing = await this.standingOrderRepository.findAll();
      const activeCount = existing.filter((o) => o.status === 'active').length;
      if (activeCount >= tenant.maxStandingOrders) {
        throw new HttpException(
          { message: `Standing order quota exceeded (${activeCount}/${tenant.maxStandingOrders}). Upgrade your plan to create more.`, code: 'QUOTA_EXCEEDED' },
          HttpStatus.TOO_MANY_REQUESTS,
        );
      }
    }

    const order = new StandingOrder();
    order.id = crypto.randomUUID();
    order.tenantId = body.tenantId;
    order.status = 'active';
    order.validFrom = body.validFrom ? new Date(body.validFrom) : new Date();
    order.validUntil = body.validUntil ? new Date(body.validUntil) : undefined;
    order.createdBy = body.createdBy;
    order.createdAt = new Date();
    order.updatedAt = new Date();
    this.mapBodyToOrder(order, body);
    const saved = await this.standingOrderRepository.save(order);
    return this.serializeOrder(saved);
  }

  @Get(':id')
  async getOrder(@Param('id') id: string) {
    const order = await this.standingOrderRepository.findById(id);
    if (!order) {
      throw new NotFoundException(`Standing order ${id} not found`);
    }
    return this.serializeOrder(order);
  }

  @Put(':id')
  async updateOrder(@Param('id') id: string, @Body() body: any) {
    const order = await this.standingOrderRepository.findById(id);
    if (!order) {
      throw new NotFoundException(`Standing order ${id} not found`);
    }
    this.mapBodyToOrder(order, body);
    order.updatedAt = new Date();
    const saved = await this.standingOrderRepository.save(order);
    return this.serializeOrder(saved);
  }

  @Patch(':id/status')
  async updateStatus(@Param('id') id: string, @Body() body: { status: string }) {
    const order = await this.standingOrderRepository.findById(id);
    if (!order) {
      throw new NotFoundException(`Standing order ${id} not found`);
    }
    order.status = body.status as StandingOrder['status'];
    order.updatedAt = new Date();
    return this.standingOrderRepository.save(order);
  }

  @Get(':id/executions')
  async getExecutions(@Param('id') id: string) {
    const order = await this.standingOrderRepository.findById(id);
    if (!order) {
      throw new NotFoundException(`Standing order ${id} not found`);
    }
    return this.executionRepository.findByOrderId(id);
  }
}
