import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';

type ProductOverviewItem = {
  id: string;
  name: string;
  description?: string;
  createdAt?: string;
  updatedAt?: string;
  projects?: { id: string; name: string; description?: string; createdAt?: string }[];
  versions?: {
    id: string;
    productId?: string;
    projectId?: string | null;
    name: string;
    status?: string;
    currentStage?: string | null;
    startDate?: string | null;
    expectedReleaseDate?: string | null;
    releaseDate?: string | null;
    members?: unknown[];
    progress?: unknown[];
    priority?: string | number | null;
    links?: unknown;
    createdAt?: string;
  }[];
  _count?: { requirements?: number; projects?: number; versions?: number };
};

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

  create(dto: CreateProductDto) {
    return this.prisma.product.create({ data: dto });
  }

  async findAll() {
    const products = await this.prisma.product.findMany({
      orderBy: { createdAt: 'desc' },
      include: {
        _count: { select: { requirements: true, projects: true, versions: true } },
      },
    });
    if (products.length > 0) return products;

    const overview = await this.getAppDataOverview();
    if (!overview) return [];
    return overview.map((product) => {
      const normalized = this.normalizeOverviewProduct(product);
      const { projects: _projects, versions: _versions, ...rest } = normalized;
      return rest;
    });
  }

  async findAllWithChildren() {
    const products = await this.prisma.product.findMany({
      orderBy: { createdAt: 'desc' },
      include: {
        projects: {
          orderBy: { createdAt: 'desc' },
          select: { id: true, name: true, description: true, createdAt: true },
        },
        versions: {
          orderBy: { createdAt: 'desc' },
          select: {
            id: true,
            productId: true,
            projectId: true,
            name: true,
            description: true,
            status: true,
            currentStage: true,
            startDate: true,
            expectedReleaseDate: true,
            releaseDate: true,
            members: true,
            progress: true,
            priority: true,
            links: true,
            createdAt: true,
            updatedAt: true,
          },
        },
        _count: { select: { requirements: true, projects: true, versions: true } },
      },
    });
    if (products.length > 0) return products.map((product) => this.normalizeRelationProduct(product));

    const overview = await this.getAppDataOverview();
    if (!overview) return [];
    return overview.map((product) => this.normalizeOverviewProduct(product));
  }

  async findOne(id: string) {
    const product = await this.prisma.product.findUnique({
      where: { id },
      include: {
        requirements: { orderBy: { createdAt: 'desc' } },
        projects: {
          orderBy: { createdAt: 'desc' },
          select: { id: true, name: true, description: true, createdAt: true },
        },
        versions: { orderBy: { createdAt: 'desc' } },
        _count: { select: { projects: true } },
      },
    });
    if (product) return product;

    const overview = await this.getAppDataOverview();
    const fallback = overview?.find((item) => item.id === id);
    if (!fallback) throw new NotFoundException('产品不存在');
    return {
      ...this.normalizeOverviewProduct(fallback),
      requirements: [],
    };
  }

  async update(id: string, dto: UpdateProductDto) {
    await this.ensureExists(id);
    return this.prisma.product.update({ where: { id }, data: dto });
  }

  async remove(id: string) {
    await this.ensureExists(id);
    return this.prisma.product.delete({ where: { id } });
  }

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

  private async getAppDataOverview(): Promise<ProductOverviewItem[] | null> {
    const row = await this.prisma.appData.findUnique({
      where: { key: 'products-overview' },
    });
    if (!Array.isArray(row?.value)) return null;
    return row.value as unknown as ProductOverviewItem[];
  }

  private normalizeOverviewProduct(product: ProductOverviewItem) {
    const projects = product.projects ?? [];
    const versions = product.versions ?? [];
    return {
      ...product,
      description: product.description ?? '',
      projects,
      versions,
      _count: {
        requirements: product._count?.requirements ?? 0,
        projects: projects.length,
        versions: versions.length,
      },
    };
  }

  private normalizeRelationProduct(product: any) {
    const projects = product.projects ?? [];
    const versions = (product.versions ?? []).map((version: any) => ({
      ...version,
      createdAt: toIso(version.createdAt),
      updatedAt: toIso(version.updatedAt),
      startDate: toIsoOrNull(version.startDate),
      expectedReleaseDate: toIsoOrNull(version.expectedReleaseDate),
      releaseDate: toIsoOrNull(version.releaseDate),
      members: Array.isArray(version.members) ? version.members : [],
      progress: Array.isArray(version.progress) ? version.progress : [],
      priority: toPriorityLabel(version.priority),
      links: isPlainObject(version.links) ? version.links : {},
    }));

    return {
      ...product,
      createdAt: toIso(product.createdAt),
      updatedAt: toIso(product.updatedAt),
      projects: projects.map((project: any) => ({ ...project, createdAt: toIso(project.createdAt) })),
      versions,
      _count: {
        requirements: product._count?.requirements ?? 0,
        projects: projects.length,
        versions: versions.length,
      },
    };
  }
}

function toIso(value: Date | string | undefined): string {
  if (value instanceof Date) return value.toISOString();
  return value ?? new Date(0).toISOString();
}

function toIsoOrNull(value: Date | string | null | undefined): string | null {
  if (!value) return null;
  if (value instanceof Date) return value.toISOString();
  return value;
}

function toPriorityLabel(value: number | null | undefined): string | undefined {
  if (typeof value !== 'number') return undefined;
  return `P${Math.max(0, Math.min(4, Math.floor(value)))}`;
}

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}
