import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateVersionDto } from './dto/create-version.dto';
import { UpdateVersionDto } from './dto/update-version.dto';

@Injectable()
export class VersionService {
  constructor(private readonly prisma: PrismaService) {}

  async create(productId: string, dto: CreateVersionDto, scopedProjectId?: string) {
    await this.ensureProductExists(productId);
    const projectId = scopedProjectId ?? dto.projectId;
    if (projectId) await this.ensureProjectInProduct(productId, projectId);

    const data = this.toVersionData(dto);
    return this.prisma.version.create({
      data: {
        ...data,
        productId,
        projectId,
        name: dto.name,
      } as any,
    });
  }

  findAll(productId: string, projectId?: string) {
    return this.prisma.version.findMany({
      where: { productId, ...(projectId ? { projectId } : {}) },
      orderBy: { createdAt: 'desc' },
    });
  }

  async update(productId: string, versionId: string, dto: UpdateVersionDto) {
    await this.ensureVersionInProduct(productId, versionId);
    if (dto.projectId) await this.ensureProjectInProduct(productId, dto.projectId);
    return this.prisma.version.update({
      where: { id: versionId },
      data: this.toVersionData(dto) as any,
    });
  }

  async remove(productId: string, versionId: string) {
    await this.ensureVersionInProduct(productId, versionId);
    return this.prisma.$transaction(async (tx) => {
      await tx.requirement.updateMany({
        where: { productId, versionId },
        data: { versionId: null },
      });
      await tx.versionPlan.deleteMany({ where: { versionId } });
      await tx.devTask.deleteMany({ where: { versionId } });
      await tx.testCase.deleteMany({ where: { versionId } });
      await tx.bug.deleteMany({ where: { versionId } });
      return tx.version.delete({ where: { id: versionId } });
    });
  }

  private toVersionData(dto: CreateVersionDto | UpdateVersionDto) {
    return {
      ...(dto.projectId !== undefined && { projectId: dto.projectId }),
      ...(dto.name !== undefined && { name: dto.name }),
      ...(dto.description !== undefined && { description: dto.description }),
      ...(dto.status !== undefined && { status: dto.status }),
      ...(dto.currentStage !== undefined && { currentStage: dto.currentStage }),
      ...(dto.startDate !== undefined && { startDate: parseOptionalDate(dto.startDate) }),
      ...(dto.expectedReleaseDate !== undefined && { expectedReleaseDate: parseOptionalDate(dto.expectedReleaseDate) }),
      ...(dto.releaseDate !== undefined && { releaseDate: parseOptionalDate(dto.releaseDate) }),
      ...(dto.members !== undefined && { members: dto.members }),
      ...(dto.progress !== undefined && { progress: dto.progress }),
      ...(dto.priority !== undefined && { priority: priorityToNumber(dto.priority) }),
      ...(dto.links !== undefined && { links: dto.links }),
    };
  }

  private async ensureProductExists(productId: string) {
    const product = await this.prisma.product.findUnique({ where: { id: productId } });
    if (!product) throw new NotFoundException('产品不存在');
  }

  private async ensureProjectInProduct(productId: string, projectId: string) {
    const project = await this.prisma.project.findFirst({ where: { id: projectId, productId } });
    if (!project) throw new NotFoundException('项目不存在');
  }

  private async ensureVersionInProduct(productId: string, versionId: string) {
    const version = await this.prisma.version.findFirst({ where: { id: versionId, productId } });
    if (!version) throw new NotFoundException('版本不存在');
  }
}

function parseOptionalDate(value: string | null | undefined): Date | null {
  if (!value) return null;
  return new Date(value);
}

function priorityToNumber(value: string | number | null | undefined): number | null {
  if (value === null || value === undefined || value === '') return null;
  if (typeof value === 'number') return Number.isFinite(value) ? value : null;
  const match = /^P([0-4])$/i.exec(value.trim());
  if (match) return Number(match[1]);
  const parsed = Number(value);
  return Number.isFinite(parsed) ? Math.max(0, Math.min(4, Math.floor(parsed))) : null;
}
