"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.routeLabelSequenceLogs = exports.reserveRouteLabelSequence = exports.routeLabelSequenceStatuses = exports.routeLabelReleaseGoodsInExecutingWorksheet = void 0;
const typeorm_1 = require("typeorm");
const route_label_sequence_counter_1 = require("../../../entities/route-label-sequence-counter");
const route_label_sequence_log_1 = require("../../../entities/route-label-sequence-log");
// Statuses to exclude from route label printing
const EXCLUDED_RO_STATUSES = ['DONE', 'PENDING_CANCEL', 'CANCELLED'];
function ensureSeqField(field) {
    if (!['refNo', 'refNo2', 'refNo3'].includes(field)) {
        throw new Error('Invalid sequence field. Allowed: refNo, refNo2, refNo3');
    }
}
function parseNonNegativeInt(value) {
    const n = parseInt(value !== null && value !== void 0 ? value : '0', 10);
    return Number.isFinite(n) ? Math.max(0, n) : 0;
}
exports.routeLabelReleaseGoodsInExecutingWorksheet = {
    async routeLabelReleaseGoodsInExecutingWorksheet(_, { partnerId, refNo, worksheetType, seqField }, context) {
        const { domain, tx } = context.state;
        let filterRefNo = '';
        if (refNo) {
            const removeSymbol = refNo
                .split('')
                .filter(res => res !== '%')
                .join('');
            const filterInfos = removeSymbol
                .toLowerCase()
                .split(',')
                .map(value => "'%" + value.trim().replace(/'/g, "''") + "%'")
                .join(',');
            filterRefNo = ` and (
        lower(rg.ref_no) like any(array[${filterInfos}])
        or lower(rg.ref_no_2) like any(array[${filterInfos}])
        or lower(rg.ref_no_3) like any(array[${filterInfos}])
      )`;
        }
        const rows = await tx.query(`
      select
        rg.id as "id",
        rg.name as "name",
        rg.ref_no as "refNo",
        rg.ref_no_2 as "refNo2",
        rg.ref_no_3 as "refNo3",
        rg.route_id as "routeId",
        rg.store_id as "storeId",
        rg.store_name as "storeName",
        rg.stop_id as "stopId",
        rg.created_at as "createdAt"
      from release_goods rg
      where rg.domain_id = $1
        and rg.bizplace_id = $2
        and rg.status not in ($3, $4, $5)
        ${filterRefNo}
      order by rg.created_at desc
      `, [domain.id, partnerId, ...EXCLUDED_RO_STATUSES]);
        const releaseGoods = rows || [];
        // If seqField is provided, fetch and merge sequence data
        if (seqField && ['refNo', 'refNo2', 'refNo3'].includes(seqField) && releaseGoods.length > 0) {
            const releaseGoodIds = releaseGoods.map((rg) => rg.id);
            // Fetch lastSeq from counters
            const lastSeqById = new Map();
            const counterRepo = tx.getRepository(route_label_sequence_counter_1.RouteLabelSequenceCounter);
            const counters = await counterRepo.find({
                where: { domain, releaseGoodId: (0, typeorm_1.In)(releaseGoodIds), field: seqField }
            });
            for (const c of counters) {
                lastSeqById.set(c.releaseGoodId, parseNonNegativeInt(c.lastSeq));
            }
            // Fetch lastPrintedBy from logs (most recent per releaseGood)
            const lastPrintedById = new Map();
            const logRepo = tx.getRepository(route_label_sequence_log_1.RouteLabelSequenceLog);
            const logs = await logRepo.find({
                where: { domain, releaseGoodId: (0, typeorm_1.In)(releaseGoodIds), field: seqField },
                relations: ['creator'],
                order: { createdAt: 'DESC' },
                take: 500
            });
            for (const log of logs) {
                if (!lastPrintedById.has(log.releaseGoodId) && log.creator) {
                    lastPrintedById.set(log.releaseGoodId, log.creator.name || '');
                }
            }
            // Merge sequence data into release goods
            return releaseGoods.map((rg) => (Object.assign(Object.assign({}, rg), { lastSeq: lastSeqById.get(rg.id) || 0, lastPrintedBy: lastPrintedById.get(rg.id) || null })));
        }
        return releaseGoods;
    }
};
exports.routeLabelSequenceStatuses = {
    async routeLabelSequenceStatuses(_, { releaseGoodIds, field }, context) {
        const { domain, tx } = context.state;
        ensureSeqField(field);
        const lastSeqById = new Map();
        const counterRepo = tx.getRepository(route_label_sequence_counter_1.RouteLabelSequenceCounter);
        const counters = await counterRepo.find({
            where: { domain, releaseGoodId: (0, typeorm_1.In)(releaseGoodIds), field }
        });
        for (const c of counters) {
            lastSeqById.set(c.releaseGoodId, parseNonNegativeInt(c.lastSeq));
        }
        const histById = new Map();
        const lastPrintedById = new Map();
        const logRepo = tx.getRepository(route_label_sequence_log_1.RouteLabelSequenceLog);
        const logs = await logRepo.find({
            where: { domain, releaseGoodId: (0, typeorm_1.In)(releaseGoodIds), field },
            relations: ['creator'],
            order: { createdAt: 'DESC' },
            take: 500
        });
        for (const log of logs) {
            const list = histById.get(log.releaseGoodId) || [];
            if (list.length >= 5)
                continue;
            list.push(`${log.from}-${log.to}`);
            histById.set(log.releaseGoodId, list);
            // Store the creator name from the most recent log entry (first one due to DESC order)
            if (!lastPrintedById.has(log.releaseGoodId) && log.creator) {
                lastPrintedById.set(log.releaseGoodId, log.creator.name || '');
            }
        }
        return releaseGoodIds.map((releaseGoodId) => {
            return {
                releaseGoodId,
                field,
                lastSeq: lastSeqById.get(releaseGoodId) || 0,
                history: histById.get(releaseGoodId) || [],
                lastPrintedBy: lastPrintedById.get(releaseGoodId) || null
            };
        });
    }
};
exports.reserveRouteLabelSequence = {
    async reserveRouteLabelSequence(_, { releaseGoodId, field, qty }, context) {
        const { domain, tx, user } = context.state;
        ensureSeqField(field);
        const q = parseNonNegativeInt(qty);
        if (q <= 0) {
            throw new Error('Print qty must be greater than 0');
        }
        const counterRepo = tx.getRepository(route_label_sequence_counter_1.RouteLabelSequenceCounter);
        const logRepo = tx.getRepository(route_label_sequence_log_1.RouteLabelSequenceLog);
        // Lock counter row to avoid duplicated sequences under concurrent prints.
        let counter = await counterRepo
            .createQueryBuilder('c')
            .setLock('pessimistic_write')
            .where('c.domain = :domainId and c.releaseGoodId = :releaseGoodId and c.field = :field', {
            domainId: domain.id,
            releaseGoodId,
            field
        })
            .getOne();
        if (!counter) {
            try {
                await counterRepo.save({
                    domain,
                    releaseGoodId,
                    field,
                    lastSeq: 0,
                    creator: user,
                    updater: user
                });
            }
            catch (e) {
                // ignore: concurrent insert can violate unique index
            }
            counter = await counterRepo
                .createQueryBuilder('c')
                .setLock('pessimistic_write')
                .where('c.domain = :domainId and c.releaseGoodId = :releaseGoodId and c.field = :field', {
                domainId: domain.id,
                releaseGoodId,
                field
            })
                .getOne();
        }
        if (!counter) {
            throw new Error('Failed to initialize route label sequence counter');
        }
        const last = parseNonNegativeInt(counter.lastSeq);
        const from = last + 1;
        const to = last + q;
        await counterRepo.update(counter.id, { lastSeq: to, updater: user, updatedAt: new Date() });
        await logRepo.save({ domain, releaseGoodId, field, from, to, qty: q, creator: user });
        return { releaseGoodId, field, from, to };
    }
};
exports.routeLabelSequenceLogs = {
    async routeLabelSequenceLogs(_, { releaseGoodId, field }, context) {
        const { domain, tx } = context.state;
        ensureSeqField(field);
        const rows = (await tx.query(`
        select
          l.id as "id",
          l.release_good_id as "releaseGoodId",
          l.field as "field",
          l."from" as "from",
          l."to" as "to",
          l.qty as "qty",
          l.created_at as "createdAt",
          u.name as "creatorName"
        from route_label_sequence_logs l
        left join users u on u.id = l.creator_id
        where l.domain_id = $1
          and l.release_good_id = $2
          and l.field = $3
        order by l.created_at desc
        limit 200
        `, [domain.id, releaseGoodId, field])) || [];
        return rows.map((row) => {
            const createdAt = row.createdAt ? new Date(row.createdAt) : null;
            return Object.assign(Object.assign({}, row), { createdAt: createdAt ? createdAt.toISOString() : null });
        });
    }
};
//# sourceMappingURL=route-label.js.map