import { type Prisma } from '@prisma/client';
import { type PrismaService } from '@modules/shared';
import { type ListingDetailData, type ListingSearchItem, type ListingSellerItem } from '../../domain/repositories/listing-read.dto';
import { type ListingSearchParams, type PaginatedResult } from '../../domain/repositories/listing.repository';

export async function findByIdWithProperty(
  prisma: PrismaService,
  id: string,
): Promise<ListingDetailData | null> {
  const listing = await prisma.listing.findUnique({
    where: { id },
    include: {
      property: {
        include: {
          media: { orderBy: { order: 'asc' }, take: 10 },
        },
      },
      seller: { select: { id: true, fullName: true, phone: true } },
      agent: { select: { id: true, userId: true, agency: true } },
    },
  });

  if (!listing) return null;

  return {
    id: listing.id,
    status: listing.status,
    transactionType: listing.transactionType,
    priceVND: listing.priceVND.toString(),
    pricePerM2: listing.pricePerM2,
    rentPriceMonthly: listing.rentPriceMonthly?.toString() ?? null,
    commissionPct: listing.commissionPct,
    viewCount: listing.viewCount,
    saveCount: listing.saveCount,
    inquiryCount: listing.inquiryCount,
    publishedAt: listing.publishedAt?.toISOString() ?? null,
    createdAt: listing.createdAt.toISOString(),
    property: {
      id: listing.property.id,
      propertyType: listing.property.propertyType,
      title: listing.property.title,
      description: listing.property.description,
      address: listing.property.address,
      ward: listing.property.ward,
      district: listing.property.district,
      city: listing.property.city,
      areaM2: listing.property.areaM2,
      bedrooms: listing.property.bedrooms,
      bathrooms: listing.property.bathrooms,
      floors: listing.property.floors,
      direction: listing.property.direction,
      yearBuilt: listing.property.yearBuilt,
      legalStatus: listing.property.legalStatus,
      amenities: listing.property.amenities,
      projectName: listing.property.projectName,
      media: listing.property.media.map((m) => ({
        id: m.id,
        url: m.url,
        type: m.type,
        order: m.order,
        caption: m.caption,
      })),
    },
    seller: listing.seller,
    agent: listing.agent,
  };
}

export async function searchListings(
  prisma: PrismaService,
  params: ListingSearchParams,
): Promise<PaginatedResult<ListingSearchItem>> {
  const page = params.page ?? 1;
  const limit = Math.min(params.limit ?? 20, 100);
  const skip = (page - 1) * limit;

  const where: Prisma.ListingWhereInput = {};

  if (params.status) where.status = params.status;
  if (params.transactionType) where.transactionType = params.transactionType;
  if (params.minPrice || params.maxPrice) {
    where.priceVND = {};
    if (params.minPrice) where.priceVND.gte = params.minPrice;
    if (params.maxPrice) where.priceVND.lte = params.maxPrice;
  }

  if (params.propertyType || params.city || params.district || params.minArea || params.maxArea || params.bedrooms) {
    where.property = {};
    if (params.propertyType) where.property.propertyType = params.propertyType;
    if (params.city) where.property.city = params.city;
    if (params.district) where.property.district = params.district;
    if (params.minArea || params.maxArea) {
      where.property.areaM2 = {};
      if (params.minArea) where.property.areaM2.gte = params.minArea;
      if (params.maxArea) where.property.areaM2.lte = params.maxArea;
    }
    if (params.bedrooms) where.property.bedrooms = { gte: params.bedrooms };
  }

  const [data, total] = await Promise.all([
    prisma.listing.findMany({
      where,
      skip,
      take: limit,
      orderBy: { createdAt: 'desc' },
      include: {
        property: {
          include: {
            media: { orderBy: { order: 'asc' }, take: 1 },
          },
        },
        seller: { select: { id: true, fullName: true } },
      },
    }),
    prisma.listing.count({ where }),
  ]);

  return {
    data: data.map((listing) => ({
      id: listing.id,
      status: listing.status,
      transactionType: listing.transactionType,
      priceVND: listing.priceVND.toString(),
      pricePerM2: listing.pricePerM2,
      viewCount: listing.viewCount,
      publishedAt: listing.publishedAt?.toISOString() ?? null,
      property: {
        id: listing.property.id,
        propertyType: listing.property.propertyType,
        title: listing.property.title,
        address: listing.property.address,
        district: listing.property.district,
        city: listing.property.city,
        areaM2: listing.property.areaM2,
        bedrooms: listing.property.bedrooms,
        bathrooms: listing.property.bathrooms,
        thumbnail: listing.property.media[0]?.url ?? null,
      },
      seller: listing.seller,
    })),
    total,
    page,
    limit,
    totalPages: Math.ceil(total / limit),
  };
}

export async function findBySellerIdQuery(
  prisma: PrismaService,
  sellerId: string,
  page: number,
  limit: number,
): Promise<PaginatedResult<ListingSellerItem>> {
  const skip = (page - 1) * limit;
  const where: Prisma.ListingWhereInput = { sellerId };

  const [data, total] = await Promise.all([
    prisma.listing.findMany({
      where,
      skip,
      take: limit,
      orderBy: { createdAt: 'desc' },
      include: {
        property: {
          include: { media: { orderBy: { order: 'asc' }, take: 1 } },
        },
      },
    }),
    prisma.listing.count({ where }),
  ]);

  return {
    data: data.map((listing) => ({
      id: listing.id,
      status: listing.status,
      transactionType: listing.transactionType,
      priceVND: listing.priceVND.toString(),
      property: {
        id: listing.property.id,
        title: listing.property.title,
        district: listing.property.district,
        city: listing.property.city,
        areaM2: listing.property.areaM2,
        thumbnail: listing.property.media[0]?.url ?? null,
      },
    })),
    total,
    page,
    limit,
    totalPages: Math.ceil(total / limit),
  };
}
