import {
  BadRequestException,
  ConflictException,
  Injectable,
  Logger,
  NotFoundException,
} from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import {
  Prisma,
  RobotLifecycleStage,
  RobotLifecycleEventType,
  RobotEventRelatedType,
} from '@prisma/client';
import { isTransitionAllowed, STAGE_TRANSITIONS } from './stage-transitions.constants';
import { LifecycleGuardsService } from './lifecycle-guards.service';
import { SnapshotProjectorService } from './snapshot-projector.service';

export interface ChangeStageInput {
  robotUnitId: string;
  toStage: RobotLifecycleStage;
  actorUserId: string;
  reason?: string;
  metadata?: Record<string, any>;
  expectedVersion?: number;
  relatedType?: RobotEventRelatedType;
  relatedId?: string;
}

/**
 * 生命周期编排服务
 *
 * 核心方法 changeStage：
 *   1. default-deny + 显式 allow（查 STAGE_TRANSITIONS 白名单）
 *   2. 触发对应 Guard
 *   3. prisma.$transaction：创建 LifecycleEvent + 刷 Snapshot
 *   4. 乐观锁：版本冲突抛 ConflictException
 */
@Injectable()
export class RobotLifecycleService {
  private readonly logger = new Logger(RobotLifecycleService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly guards: LifecycleGuardsService,
    private readonly projector: SnapshotProjectorService,
  ) {}

  /**
   * 状态切换 — 主入口
   */
  async changeStage(input: ChangeStageInput) {
    const unit = await this.prisma.robotUnit.findFirst({
      where: { id: input.robotUnitId, deletedAt: null },
      include: { snapshot: true },
    });
    if (!unit) throw new NotFoundException(`Robot unit ${input.robotUnitId} not found`);

    const snapshot = unit.snapshot;
    if (!snapshot) {
      throw new BadRequestException(
        `Robot unit ${input.robotUnitId} 缺少 snapshot（数据完整性异常）`,
      );
    }

    const fromStage = snapshot.currentStage;
    const toStage = input.toStage;

    if (fromStage === toStage) {
      throw new BadRequestException(`机器人已处于 ${toStage} 状态`);
    }

    // 1. 白名单
    if (!isTransitionAllowed(fromStage, toStage)) {
      throw new BadRequestException(
        `非法状态转换：${fromStage} → ${toStage}（允许: ${(STAGE_TRANSITIONS[fromStage] ?? []).join(', ') || '<终态>'}）`,
      );
    }

    // 2. Guard
    const guardResult = await this.guards.check(this.prisma, input.robotUnitId, fromStage, toStage);
    if (!guardResult.ok) {
      throw new BadRequestException({
        message: `Guard 检查失败：${(guardResult.reasons ?? []).join('；')}`,
        reasons: guardResult.reasons,
      });
    }

    // 3. 乐观锁版本检查
    if (input.expectedVersion !== undefined && snapshot.version !== input.expectedVersion) {
      throw new ConflictException({
        message: `Snapshot 版本冲突（期望 ${input.expectedVersion}，实际 ${snapshot.version}）`,
      });
    }

    // 4. 同事务：创建 event + 刷 snapshot
    return this.prisma.$transaction(async (tx) => {
      const event = await tx.robotLifecycleEvent.create({
        data: {
          robotUnitId: input.robotUnitId,
          eventType: RobotLifecycleEventType.stage_changed,
          fromStage,
          toStage,
          actorUserId: input.actorUserId,
          relatedType: input.relatedType,
          relatedId: input.relatedId,
          payload: (input.metadata ?? {}) as Prisma.InputJsonValue,
          notes: input.reason,
          occurredAt: new Date(),
          organizationId: unit.organizationId,
          createdById: input.actorUserId,
        },
      });

      await this.projector.project(tx, event, snapshot.version);

      this.logger.log(
        `stage_changed unit=${input.robotUnitId} ${fromStage} → ${toStage} by=${input.actorUserId}`,
      );

      // 避免一次额外 round-trip：snapshot 已被 projector 刷为目标 stage + version+1
      return {
        ...unit,
        snapshot: {
          ...snapshot,
          currentStage: toStage,
          lastEventId: event.id,
          lastEventAt: event.occurredAt,
          version: snapshot.version + 1,
        },
      };
    });
  }

  /**
   * 批量切换 — 各自独立事务，失败不影响其他；并行执行
   */
  async bulkChangeStage(
    robotUnitIds: string[],
    toStage: RobotLifecycleStage,
    actorUserId: string,
    reason?: string,
  ) {
    const results = await Promise.allSettled(
      robotUnitIds.map((id) =>
        this.changeStage({ robotUnitId: id, toStage, actorUserId, reason }),
      ),
    );
    const success: string[] = [];
    const failed: Array<{ id: string; reason: string; reasons?: string[] }> = [];
    results.forEach((r, idx) => {
      const id = robotUnitIds[idx];
      if (r.status === 'fulfilled') {
        success.push(id);
      } else {
        const e: any = r.reason;
        // 保留 Guard 失败的 reasons[] 结构供前端展示
        const reasons = e?.response?.reasons ?? e?.reasons;
        failed.push({ id, reason: e?.message ?? 'Unknown error', reasons });
      }
    });
    return { success, failed };
  }

  /**
   * Hold / Unhold（不切 stage，仅刷 isHeld 标记）
   */
  async hold(robotUnitId: string, holdReason: string, actorUserId: string) {
    const unit = await this.prisma.robotUnit.findFirst({
      where: { id: robotUnitId, deletedAt: null },
      include: { snapshot: true },
    });
    if (!unit || !unit.snapshot) throw new NotFoundException(`Robot unit ${robotUnitId} 或 snapshot 不存在`);

    return this.prisma.$transaction(async (tx) => {
      const event = await tx.robotLifecycleEvent.create({
        data: {
          robotUnitId,
          eventType: RobotLifecycleEventType.held,
          actorUserId,
          payload: { holdReason } as Prisma.InputJsonValue,
          occurredAt: new Date(),
          organizationId: unit.organizationId,
          createdById: actorUserId,
        },
      });
      await this.projector.project(tx, event, unit.snapshot!.version);
      return event;
    });
  }

  async unhold(robotUnitId: string, actorUserId: string) {
    const unit = await this.prisma.robotUnit.findFirst({
      where: { id: robotUnitId, deletedAt: null },
      include: { snapshot: true },
    });
    if (!unit || !unit.snapshot) throw new NotFoundException(`Robot unit ${robotUnitId} 或 snapshot 不存在`);

    return this.prisma.$transaction(async (tx) => {
      const event = await tx.robotLifecycleEvent.create({
        data: {
          robotUnitId,
          eventType: RobotLifecycleEventType.unheld,
          actorUserId,
          payload: {} as Prisma.InputJsonValue,
          occurredAt: new Date(),
          organizationId: unit.organizationId,
          createdById: actorUserId,
        },
      });
      await this.projector.project(tx, event, unit.snapshot!.version);
      return event;
    });
  }

  /**
   * 位置切换
   */
  async moveLocation(
    robotUnitId: string,
    toLocationId: string,
    actorUserId: string,
    reason?: string,
  ) {
    const unit = await this.prisma.robotUnit.findFirst({
      where: { id: robotUnitId, deletedAt: null },
      include: { snapshot: true },
    });
    if (!unit || !unit.snapshot) throw new NotFoundException(`Robot unit ${robotUnitId} 或 snapshot 不存在`);

    const fromLocationId = unit.snapshot.currentLocationId;
    return this.prisma.$transaction(async (tx) => {
      const event = await tx.robotLifecycleEvent.create({
        data: {
          robotUnitId,
          eventType: RobotLifecycleEventType.location_moved,
          fromLocationId,
          toLocationId,
          actorUserId,
          notes: reason,
          payload: {} as Prisma.InputJsonValue,
          occurredAt: new Date(),
          organizationId: unit.organizationId,
          createdById: actorUserId,
        },
      });
      await this.projector.project(tx, event, unit.snapshot!.version);
      return event;
    });
  }

  /**
   * 查询事件流（最近 N 条）
   */
  async listEvents(robotUnitId: string, limit = 50) {
    return this.prisma.robotLifecycleEvent.findMany({
      where: { robotUnitId, deletedAt: null },
      orderBy: { occurredAt: 'desc' },
      take: limit,
    });
  }

  /**
   * 查询合法的下一步 stage
   */
  getAllowedNextStages(currentStage: RobotLifecycleStage): RobotLifecycleStage[] {
    return STAGE_TRANSITIONS[currentStage] ?? [];
  }
}
