import chalk from 'chalk';
import { client } from '../src/db/typesense.ts';
import type { DBInstance } from '../src/db/index.ts';
import fs from "node:fs/promises";
import { sql } from 'drizzle-orm'

import * as util from 'util';


const DollarToInt = (dollar: any) => {
    if (dollar === null) return null;
    return Math.round(dollar * 100);
}

export type Logger = (msg: string) => void;
const defaultLogger: Logger = (msg) => console.log(chalk.green(msg));



export const Sleep = (ms: number) => {
  return new Promise(resolve => setTimeout(resolve, ms));
}


export const FileExists = async (path: string): Promise<boolean> => {
  try {
    await fs.access(path);
    return true;
  } catch {
    return false;
  }
}


export const GetNumberOrNull = (value: any): number | null => {
  const number = Number(value); // Attempt to convert the value to a number
  if (Number.isNaN(number)) {
    return null; // Return null if the result is NaN
  }
  return number; // Otherwise, return the number
}


// Delete and recreate the 'cards' index
export const createCardCollection = async (log: Logger = defaultLogger) => {
    try {
        await client.collections('cards').delete();
    } catch (error) {
        // Ignore error, just means collection doesn't exist
    }
    await client.collections().create({
        name: 'cards',
        fields: [
            { name: 'id', type: 'string' },
            { name: 'cardId', type: 'int32' },
            { name: 'productId', type: 'int32' },
            { name: 'variant', type: 'string', facet: true },
            { name: 'productName', type: 'string' },
            { name: 'productLineName', type: 'string', facet: true },
            { name: 'rarityName', type: 'string', facet: true },
            { name: 'setName', type: 'string', facet: true },
            { name: 'setCode', type: 'string' },
            { name: 'cardType', type: 'string', facet: true },
            { name: 'energyType', type: 'string', facet: true },
            { name: 'number', type: 'string' },
            { name: 'inumber', type: 'int32', optional: true, sort: true },
            { name: 'Artist', type: 'string' },
            { name: 'sealed', type: 'bool' },
            { name: 'releaseDate', type: 'int32' },
            { name: 'marketPrice', type: 'int32', optional: true, sort: true },
            { name: 'content', type: 'string', token_separators: ['/'] },
            // { name: 'sku_id', type: 'string[]', optional: true, reference: 'skus.id', async_reference: true }
        ],
    });
    log('Collection "cards" created successfully.');
}

// Delete and recreate the 'skus' index
export const createSkuCollection = async (log: Logger = defaultLogger) => {
    try {
        await client.collections('skus').delete();
    } catch (error) {
        // Ignore error, just means collection doesn't exist
    }
    await client.collections().create({
        name: 'skus',
        fields: [
            { name: 'id', type: 'string' },
            { name: 'condition', type: 'string' },
            { name: 'highestPrice', type: 'int32', optional: true },
            { name: 'lowestPrice', type: 'int32', optional: true },
            { name: 'marketPrice', type: 'int32', optional: true },
            { name: 'card_id', type: 'string', reference: 'cards.id', async_reference: true },
        ]
    });
    log('Collection "skus" created successfully.');
}

// Delete and recreate the 'inventory' index
export const createInventoryCollection = async (log: Logger = defaultLogger) => {
    try {
        await client.collections('inventories').delete();
    } catch (error) {
        // Ignore error, just means collection doesn't exist
    }
    await client.collections().create({
        name: 'inventories',
        fields: [
            { name: 'id', type: 'string' },
            { name: 'userId', type: 'string' },
            { name: 'catalogName', type: 'string' },
            { name: 'card_id', type: 'string', reference: 'cards.id', async_reference: true },
            { name: 'sku_id', type: 'string', reference: 'skus.id', async_reference: true },
            { name: 'purchasePrice', type: 'int32', optional: true },
            // content,setName,productLineName,rarityName,energyType,cardType from cards for searching
            { name: 'content', type: 'string', token_separators: ['/'] },
            { name: 'setName', type: 'string' },
            { name: 'productLineName', type: 'string' },
            { name: 'rarityName', type: 'string' },
            { name: 'energyType', type: 'string' },
            { name: 'cardType', type: 'string' },
        ]
    });
    log('Collection "inventories" created successfully.');
}


export const upsertCardCollection = async (db:DBInstance, log: Logger = defaultLogger) => {
    const pokemon = await db.query.cards.findMany({
        with: { set: true, tcgdata: true, prices: true },
    });
    await client.collections('cards').documents().import(pokemon.map(card => {
        // Use the NM SKU price matching the card's variant (kept fresh by syncPrices)
        // Fall back to any NM sku, then to tcgdata price
        const nmSku = card.prices.find(p => p.condition === 'Near Mint' && p.variant === card.variant)
                   ?? card.prices.find(p => p.condition === 'Near Mint');
        const marketPrice = nmSku?.marketPrice
            ? DollarToInt(nmSku.marketPrice)
            : card.tcgdata?.marketPrice
                ? DollarToInt(card.tcgdata.marketPrice)
                : null;

        return {
            id: card.cardId.toString(),
            cardId: card.cardId,
            productId: card.productId,
            variant: card.variant,
            productName: card.productName,
            productLineName: card.productLineName,
            rarityName: card.rarityName,
            setName: card.set?.setName || "",
            setCode: card.set?.setCode || "",
            cardType: card.cardType || "",
            energyType: card.energyType || "",
            number: card.number,
            inumber: (card.number !== null) ? parseInt(card.number) : undefined,
            Artist: card.artist || "",
            sealed: card.sealed,
            content: [card.productName, card.productLineName, card.set?.setName || "", card.set?.setCode || "", card.number, card.rarityName, card.artist || ""].join(' '),
            releaseDate: card.tcgdata?.releaseDate ? Math.floor(new Date(card.tcgdata.releaseDate).getTime() / 1000) : 0,
            ...(marketPrice !== null && { marketPrice }),
            // sku_id: card.prices.map(price => price.skuId.toString())
        };
    }), { action: 'upsert' });
    log('Collection "cards" indexed successfully.');
}

export const upsertSkuCollection = async (db:DBInstance, log: Logger = defaultLogger) => {
    const skus = await db.query.skus.findMany();
    await client.collections('skus').documents().import(skus.map(sku => ({
        id: sku.skuId.toString(),
        condition: sku.condition,
        highestPrice: DollarToInt(sku.highestPrice),
        lowestPrice: DollarToInt(sku.lowestPrice),
        marketPrice: DollarToInt(sku.marketPrice),
        card_id: sku.cardId.toString(),
    })), { action: 'upsert' }); 
    log('Collection "skus" indexed successfully.');
}

export const upsertInventoryCollection = async (db:DBInstance, log: Logger = defaultLogger) => {
    const inv = await db.query.inventory.findMany({
        with: { sku: { with: { card: { with: { set: true } } } } }
    });
    await client.collections('inventories').documents().import(inv.map(i => ({
        id: i.inventoryId,
        userId: i.userId,
        catalogName: i.catalogName,
        card_id: i.sku?.cardId.toString(),
        sku_id: i.skuId.toString(),
        purchasePrice: DollarToInt(i.purchasePrice),
        productLineName: i.sku?.card?.productLineName,
        rarityName: i.sku?.card?.rarityName,
        setName: i.sku?.card?.set?.setName || "",
        cardType: i.sku?.card?.cardType || "",
        energyType: i.sku?.card?.energyType || "",
        content: [
            i.sku?.card?.productName, 
            i.sku?.card?.productLineName, 
            i.sku?.card?.set?.setName || "", 
            i.sku?.card?.number, 
            i.sku?.card?.rarityName, 
            i.sku?.card?.artist || ""
        ].join(' '),
    })), { action: 'upsert' });
    log('Collection "inventories" indexed successfully.');
}





export const UpdateVariants = async (db:DBInstance, log: Logger = (m) => console.log(m)) => {
    const updates = await db.execute(sql`update cards as c
set 
    product_name = a.product_name, product_line_name = a.product_line_name, product_url_name = a.product_url_name, rarity_name = a.rarity_name, 
    sealed = a.sealed, set_id = a.set_id, card_type = a.card_type, energy_type = a.energy_type, number = a.number, inumber = a.inumber, artist = a.artist
from (
    select t.product_id, b.variant,
        coalesce(o.product_name, regexp_replace(regexp_replace(coalesce(nullif(t.product_name, ''), t.product_url_name),' \\\\(.*\\\\)',''),' - .*$','')) as product_name,
        coalesce(o.product_line_name, t.product_line_name) as product_line_name, coalesce(o.product_url_name, t.product_url_name) as product_url_name,
        coalesce(o.rarity_name, t.rarity_name) as rarity_name, coalesce(o.sealed, t.sealed) as sealed, coalesce(o.set_id, t.set_id) as set_id,
        coalesce(o.card_type, t.card_type) as card_type, coalesce(o.energy_type, t.energy_type) as energy_type,
        coalesce(o.number, regexp_replace(t.number,'^0+','')) as number,
        nullif(regexp_replace(regexp_replace(coalesce(o.number,t.number),'/.*',''),'[^0-9]','','g'),'')::integer as inumber,
        coalesce(o.artist, t.artist) as artist
    from tcg_cards t
    join (select distinct product_id, variant from skus) b on t.product_id = b.product_id
    left join tcg_overrides o on t.product_id = o.product_id
    ) a
where c.product_id = a.product_id and c.variant = a.variant and
(
    c.product_name is distinct from a.product_name or c.product_line_name is distinct from a.product_line_name or 
    c.product_url_name is distinct from a.product_url_name or c.rarity_name is distinct from a.rarity_name or 
    c.sealed is distinct from a.sealed or c.set_id is distinct from a.set_id or c.card_type is distinct from a.card_type or 
    c.energy_type is distinct from a.energy_type or c."number" is distinct from a."number" or c.artist is distinct from a.artist
)
`);
    log(`Updated ${updates.rowCount} rows in cards table`);

    const inserts = await db.execute(sql`insert into cards (product_id, variant, product_name, product_line_name, product_url_name, rarity_name, sealed, set_id, card_type, energy_type, "number", inumber, artist)
select t.product_id, b.variant, 
coalesce(o.product_name, regexp_replace(regexp_replace(coalesce(nullif(t.product_name, ''), t.product_url_name),' \\\\(.*\\\\)',''),' - .*$','')) as product_name,
coalesce(o.product_line_name, t.product_line_name) as product_line_name, coalesce(o.product_url_name, t.product_url_name) as product_url_name, coalesce(o.rarity_name, t.rarity_name) as rarity_name, 
coalesce(o.sealed, t.sealed) as sealed, coalesce(o.set_id, t.set_id) as set_id, coalesce(o.card_type, t.card_type) as card_type, 
coalesce(o.energy_type, t.energy_type) as energy_type, coalesce(o.number, regexp_replace(t.number,'^0+','')) as number, 
nullif(regexp_replace(regexp_replace(coalesce(o.number,t.number),'/.*',''),'[^0-9]','','g'),'')::integer as inumber, coalesce(o.artist, t.artist) as artist
from tcg_cards t
join (select distinct product_id, variant from skus) b on t.product_id = b.product_id
left join tcg_overrides o on t.product_id = o.product_id
where not exists (select 1 from cards where product_id=t.product_id and variant=b.variant)
`);
    log(`Inserted ${inserts.rowCount} rows into cards table`);

    const skuUpdates = await db.execute(sql`update skus s set card_id = c.card_id from cards c where s.product_id = c.product_id and s.variant = c.variant and s.card_id is distinct from c.card_id`);
    log(`Updated ${skuUpdates.rowCount} rows in skus table`);

}
