import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { UserRole } from '@prisma/client';
import { CurrentUser, JwtAuthGuard, Roles, RolesGuard } from '@modules/auth';
import type { JwtPayload } from '@modules/auth';
import { NotFoundException } from '@modules/shared';
import { CreateProjectCommand } from '../../application/commands/create-project/create-project.command';
import { DeleteProjectCommand } from '../../application/commands/delete-project/delete-project.command';
import { UpdateProjectCommand } from '../../application/commands/update-project/update-project.command';
import { GetProjectQuery } from '../../application/queries/get-project/get-project.query';
import { type ProjectStats } from '../../application/queries/get-project-stats/get-project-stats.handler';
import { GetProjectStatsQuery } from '../../application/queries/get-project-stats/get-project-stats.query';
import { ListProjectsQuery } from '../../application/queries/list-projects/list-projects.query';
import { CreateProjectDto } from '../dto/create-project.dto';
import { SearchProjectsDto } from '../dto/search-projects.dto';
import { UpdateProjectDto } from '../dto/update-project.dto';

interface RawProjectListItem {
  id: string;
  name: string;
  slug: string;
  developer: string;
  developerLogo: string | null;
  media?: { url: string; type?: string; order?: number }[] | null;
  [k: string]: unknown;
}

function shapeProject<T extends RawProjectListItem>(row: T) {
  const { developer, developerLogo, media, ...rest } = row;
  const thumbnailUrl =
    Array.isArray(media) && media.length > 0 ? (media[0]?.url ?? null) : null;
  return {
    ...rest,
    developer: { id: developer, name: developer, logo: developerLogo ?? null },
    thumbnailUrl,
    propertyTypes: [],
    completionDate: (rest as { completionDate?: Date | null }).completionDate ?? null,
  };
}

function shapeProjectDetail<T extends RawProjectListItem>(row: T) {
  // Detail endpoint: preserve `media` so the gallery on the frontend can
  // render, and keep the same shape as the list response for everything else.
  const shaped = shapeProject(row);
  return {
    ...shaped,
    media: Array.isArray(row.media) ? row.media : [],
  };
}

@ApiTags('projects')
@Controller('projects')
export class ProjectsController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly queryBus: QueryBus,
  ) {}

  // ── Public endpoints ──────────────────────────────────────────────

  @ApiOperation({ summary: 'Danh sách dự án', description: 'Tìm kiếm và lọc dự án bất động sản' })
  @ApiResponse({ status: 200, description: 'Danh sách dự án phân trang' })
  @Get()
  async listProjects(@Query() dto: SearchProjectsDto) {
    const result = await this.queryBus.execute<
      ListProjectsQuery,
      { data: RawProjectListItem[]; total: number; page: number; limit: number; totalPages: number }
    >(
      new ListProjectsQuery(
        dto.q,
        dto.status,
        dto.city,
        dto.district,
        dto.developer,
        dto.isVerified,
        dto.page ?? 1,
        dto.limit ?? 20,
      ),
    );
    return { ...result, data: result.data.map(shapeProject) };
  }

  // ── Developer (CĐT) endpoints ─────────────────────────────────────

  @ApiOperation({
    summary: 'Dự án của tôi (CĐT)',
    description: 'Danh sách dự án mà user hiện tại là chủ đầu tư. ADMIN dùng endpoint này để xem tất cả dự án mình đã được gán.',
  })
  @ApiResponse({ status: 200, description: 'Danh sách dự án đã lọc theo owner' })
  @ApiResponse({ status: 401, description: 'Chưa xác thực' })
  @ApiResponse({ status: 403, description: 'Không có quyền' })
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.DEVELOPER, UserRole.ADMIN)
  @Get('mine/list')
  async listMyProjects(
    @CurrentUser() user: JwtPayload,
    @Query() dto: SearchProjectsDto,
  ) {
    const result = await this.queryBus.execute<
      ListProjectsQuery,
      { data: RawProjectListItem[]; total: number; page: number; limit: number; totalPages: number }
    >(
      new ListProjectsQuery(
        dto.q,
        dto.status,
        dto.city,
        dto.district,
        dto.developer,
        dto.isVerified,
        dto.page ?? 1,
        dto.limit ?? 20,
        user.sub,
      ),
    );
    return { ...result, data: result.data.map(shapeProject) };
  }

  @ApiOperation({
    summary: 'Thống kê dự án (CĐT / admin)',
    description:
      'Trả về số listings, inquiries (chưa đọc), users đã lưu. Admin xem được mọi dự án; DEVELOPER chỉ xem được dự án của mình.',
  })
  @ApiResponse({ status: 200, description: 'Thống kê dự án' })
  @ApiResponse({ status: 401, description: 'Chưa xác thực' })
  @ApiResponse({ status: 403, description: 'Không có quyền' })
  @ApiResponse({ status: 404, description: 'Không tìm thấy dự án' })
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.DEVELOPER, UserRole.ADMIN)
  @Get(':id/stats')
  async getProjectStats(
    @CurrentUser() user: JwtPayload,
    @Param('id') id: string,
  ): Promise<ProjectStats> {
    return this.queryBus.execute(
      new GetProjectStatsQuery(id, user.sub, user.role as UserRole),
    );
  }

  @ApiOperation({ summary: 'Chi tiết dự án', description: 'Xem chi tiết dự án theo slug hoặc ID' })
  @ApiResponse({ status: 200, description: 'Thông tin chi tiết dự án' })
  @ApiResponse({ status: 404, description: 'Không tìm thấy dự án' })
  @Get(':slugOrId')
  async getProject(@Param('slugOrId') slugOrId: string) {
    const result = await this.queryBus.execute<GetProjectQuery, RawProjectListItem | null>(
      new GetProjectQuery(slugOrId),
    );
    if (!result) {
      throw new NotFoundException('Dự án', slugOrId);
    }
    return shapeProjectDetail(result);
  }

  // ── Admin endpoints ───────────────────────────────────────────────

  @ApiOperation({ summary: 'Tạo dự án', description: 'Admin tạo dự án tuỳ ý; CĐT (DEVELOPER) tạo dự án của mình (tự động gán ownerId).' })
  @ApiResponse({ status: 201, description: 'Dự án đã tạo' })
  @ApiResponse({ status: 400, description: 'Validation error' })
  @ApiResponse({ status: 401, description: 'Chưa xác thực' })
  @ApiResponse({ status: 403, description: 'Không có quyền' })
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.DEVELOPER)
  @Post()
  async createProject(
    @CurrentUser() user: JwtPayload,
    @Body() dto: CreateProjectDto,
  ) {
    // DEVELOPER callers own what they create; admin may pass ownerId in DTO
    // (not yet exposed — use PATCH to reassign) or leave unassigned.
    const ownerId = user.role === UserRole.DEVELOPER ? user.sub : null;
    return this.commandBus.execute(
      new CreateProjectCommand(
        dto.name,
        dto.slug,
        dto.developer,
        dto.developerLogo ?? null,
        dto.totalUnits,
        dto.status,
        dto.latitude,
        dto.longitude,
        dto.address,
        dto.ward,
        dto.district,
        dto.city,
        dto.description ?? null,
        dto.amenities ?? null,
        dto.masterPlanUrl ?? null,
        dto.minPrice ? BigInt(dto.minPrice) : null,
        dto.maxPrice ? BigInt(dto.maxPrice) : null,
        dto.pricePerM2Range ?? null,
        dto.totalArea ?? null,
        dto.buildingCount ?? null,
        dto.floorCount ?? null,
        dto.unitTypes ?? null,
        dto.tags ?? [],
        dto.startDate ? new Date(dto.startDate) : null,
        dto.completionDate ? new Date(dto.completionDate) : null,
        dto.suitableFor ?? [],
        dto.whyThisLocation ?? null,
        ownerId,
      ),
    );
  }

  @ApiOperation({ summary: 'Cập nhật dự án', description: 'Admin cập nhật bất kỳ dự án nào; CĐT (DEVELOPER) chỉ cập nhật dự án mình làm chủ.' })
  @ApiResponse({ status: 200, description: 'Dự án đã cập nhật' })
  @ApiResponse({ status: 401, description: 'Chưa xác thực' })
  @ApiResponse({ status: 403, description: 'Không có quyền' })
  @ApiResponse({ status: 404, description: 'Không tìm thấy tài nguyên' })
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.DEVELOPER)
  @Patch(':id')
  async updateProject(
    @CurrentUser() user: JwtPayload,
    @Param('id') id: string,
    @Body() dto: UpdateProjectDto,
  ) {
    return this.commandBus.execute(
      new UpdateProjectCommand(
        id,
        user.sub,
        user.role as UserRole,
        dto.name,
        dto.developer,
        dto.developerLogo,
        dto.totalUnits,
        dto.completedUnits,
        dto.status,
        dto.description,
        dto.amenities,
        dto.masterPlanUrl,
        dto.minPrice !== undefined ? (dto.minPrice ? BigInt(dto.minPrice) : null) : undefined,
        dto.maxPrice !== undefined ? (dto.maxPrice ? BigInt(dto.maxPrice) : null) : undefined,
        dto.pricePerM2Range,
        dto.totalArea,
        dto.buildingCount,
        dto.floorCount,
        dto.unitTypes,
        dto.media,
        dto.documents,
        dto.tags,
        dto.isVerified,
        dto.startDate !== undefined ? (dto.startDate ? new Date(dto.startDate) : null) : undefined,
        dto.completionDate !== undefined ? (dto.completionDate ? new Date(dto.completionDate) : null) : undefined,
        dto.suitableFor,
        dto.whyThisLocation,
        dto.ownerId,
      ),
    );
  }

  @ApiOperation({ summary: 'Xoá dự án', description: 'Admin xoá bất kỳ dự án nào; CĐT (DEVELOPER) chỉ xoá dự án của mình.' })
  @ApiResponse({ status: 200, description: 'Dự án đã xoá' })
  @ApiResponse({ status: 401, description: 'Chưa xác thực' })
  @ApiResponse({ status: 403, description: 'Không có quyền' })
  @ApiResponse({ status: 404, description: 'Không tìm thấy tài nguyên' })
  @ApiBearerAuth('JWT')
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles(UserRole.ADMIN, UserRole.DEVELOPER)
  @Delete(':id')
  async deleteProject(
    @CurrentUser() user: JwtPayload,
    @Param('id') id: string,
  ): Promise<{ success: true }> {
    await this.commandBus.execute(
      new DeleteProjectCommand(id, user.sub, user.role as UserRole),
    );
    return { success: true };
  }
}
