import { Injectable, type OnModuleInit } from '@nestjs/common';
import { type Client as TypesenseClient } from 'typesense';
import { type CollectionCreateSchema } from 'typesense/lib/Typesense/Collections';
import { TypesenseClientService } from '@modules/search';
import { LoggerService, PrismaService } from '@modules/shared';

export const TRANSFER_LISTINGS_COLLECTION = 'transfer_listings';

const COLLECTION_SCHEMA: CollectionCreateSchema = {
  name: TRANSFER_LISTINGS_COLLECTION,
  enable_nested_fields: false,
  token_separators: ['-', '_'],
  fields: [
    { name: 'listingId', type: 'string', facet: false },
    { name: 'title', type: 'string', facet: false },
    { name: 'description', type: 'string', facet: false, optional: true },
    { name: 'category', type: 'string', facet: true },
    { name: 'status', type: 'string', facet: true },
    { name: 'district', type: 'string', facet: true },
    { name: 'city', type: 'string', facet: true },
    { name: 'address', type: 'string', facet: false },
    { name: 'askingPriceVND', type: 'int64', facet: false },
    { name: 'isNegotiable', type: 'bool', facet: true },
    { name: 'areaM2', type: 'float', facet: false, optional: true },
    { name: 'hasPremises', type: 'bool', facet: true },
    { name: 'itemCount', type: 'int32', facet: false },
    { name: 'location', type: 'geopoint', facet: false },
    { name: 'sellerId', type: 'string', facet: false },
    { name: 'viewCount', type: 'int32', facet: false },
    { name: 'inquiryCount', type: 'int32', facet: false },
    { name: 'publishedAt', type: 'int64', facet: false, optional: true },
    { name: 'createdAt', type: 'int64', facet: false },
  ],
};

interface RawTransferListingForSync {
  id: string;
  sellerId: string;
  title: string;
  description: string | null;
  category: string;
  status: string;
  district: string;
  city: string;
  address: string;
  askingPriceVND: bigint;
  isNegotiable: boolean;
  areaM2: number | null;
  viewCount: number;
  inquiryCount: number;
  publishedAt: Date | null;
  createdAt: Date;
  lat: number;
  lng: number;
  itemCount: number;
}

@Injectable()
export class TypesenseTransferService implements OnModuleInit {
  private client: TypesenseClient | null = null;

  constructor(
    private readonly typesenseClient: TypesenseClientService,
    private readonly prisma: PrismaService,
    private readonly logger: LoggerService,
  ) {}

  async onModuleInit(): Promise<void> {
    try {
      this.client = this.typesenseClient.getClient();
      await this.ensureCollection();
      await this.syncListings();
    } catch (err) {
      this.logger.warn(`Typesense transfer init failed (non-fatal): ${err}`, 'TypesenseTransfer');
    }
  }

  private async ensureCollection(): Promise<void> {
    if (!this.client) return;

    try {
      await this.client.collections(TRANSFER_LISTINGS_COLLECTION).retrieve();
      this.logger.log(`Collection "${TRANSFER_LISTINGS_COLLECTION}" exists`, 'TypesenseTransfer');
    } catch {
      await this.client.collections().create(COLLECTION_SCHEMA);
      this.logger.log(`Collection "${TRANSFER_LISTINGS_COLLECTION}" created`, 'TypesenseTransfer');
    }
  }

  async syncListings(): Promise<void> {
    if (!this.client) return;

    const listings = await this.prisma.$queryRaw<RawTransferListingForSync[]>`
      SELECT t.id, t."sellerId", t.title, t.description,
        t.category::text, t.status::text,
        t.district, t.city, t.address,
        t."askingPriceVND", t."isNegotiable", t."areaM2",
        t."viewCount", t."inquiryCount",
        t."publishedAt", t."createdAt",
        ST_Y(t.location::geometry) as lat, ST_X(t.location::geometry) as lng,
        (SELECT COUNT(*) FROM "TransferItem" WHERE "transferListingId" = t.id)::int as "itemCount"
      FROM "TransferListing" t
      WHERE t.status = 'ACTIVE'
    `;

    if (listings.length === 0) return;

    const docs = listings.map((l) => ({
      id: l.id,
      listingId: l.id,
      title: l.title,
      description: l.description ?? undefined,
      category: l.category.toLowerCase(),
      status: l.status.toLowerCase(),
      district: l.district,
      city: l.city,
      address: l.address,
      askingPriceVND: Number(l.askingPriceVND),
      isNegotiable: l.isNegotiable,
      areaM2: l.areaM2 ?? undefined,
      hasPremises: (l.areaM2 ?? 0) > 0,
      itemCount: l.itemCount,
      location: [Number(l.lat), Number(l.lng)],
      sellerId: l.sellerId,
      viewCount: l.viewCount,
      inquiryCount: l.inquiryCount,
      publishedAt: l.publishedAt ? Math.floor(l.publishedAt.getTime() / 1000) : undefined,
      createdAt: Math.floor(l.createdAt.getTime() / 1000),
    }));

    try {
      const jsonl = docs.map((d) => JSON.stringify(d)).join('\n');
      await this.client.collections(TRANSFER_LISTINGS_COLLECTION).documents().import(jsonl, { action: 'upsert' });
      this.logger.log(`Synced ${docs.length} transfer listings to Typesense`, 'TypesenseTransfer');
    } catch (err) {
      this.logger.warn(`Transfer listing sync error: ${err}`, 'TypesenseTransfer');
    }
  }

  async indexListing(listingId: string): Promise<void> {
    if (!this.client) return;

    const [listing] = await this.prisma.$queryRaw<RawTransferListingForSync[]>`
      SELECT t.id, t."sellerId", t.title, t.description,
        t.category::text, t.status::text,
        t.district, t.city, t.address,
        t."askingPriceVND", t."isNegotiable", t."areaM2",
        t."viewCount", t."inquiryCount",
        t."publishedAt", t."createdAt",
        ST_Y(t.location::geometry) as lat, ST_X(t.location::geometry) as lng,
        (SELECT COUNT(*) FROM "TransferItem" WHERE "transferListingId" = t.id)::int as "itemCount"
      FROM "TransferListing" t
      WHERE t.id = ${listingId}
    `;

    if (!listing) return;

    const doc = {
      id: listing.id,
      listingId: listing.id,
      title: listing.title,
      description: listing.description ?? undefined,
      category: listing.category.toLowerCase(),
      status: listing.status.toLowerCase(),
      district: listing.district,
      city: listing.city,
      address: listing.address,
      askingPriceVND: Number(listing.askingPriceVND),
      isNegotiable: listing.isNegotiable,
      areaM2: listing.areaM2 ?? undefined,
      hasPremises: (listing.areaM2 ?? 0) > 0,
      itemCount: listing.itemCount,
      location: [Number(listing.lat), Number(listing.lng)],
      sellerId: listing.sellerId,
      viewCount: listing.viewCount,
      inquiryCount: listing.inquiryCount,
      publishedAt: listing.publishedAt ? Math.floor(listing.publishedAt.getTime() / 1000) : undefined,
      createdAt: Math.floor(listing.createdAt.getTime() / 1000),
    };

    await this.client.collections(TRANSFER_LISTINGS_COLLECTION).documents().upsert(doc);
  }

  async deleteListing(listingId: string): Promise<void> {
    if (!this.client) return;

    try {
      await this.client.collections(TRANSFER_LISTINGS_COLLECTION).documents(listingId).delete();
    } catch (err) {
      this.logger.warn(`Failed to delete transfer listing ${listingId} from Typesense: ${err}`, 'TypesenseTransfer');
    }
  }
}
