"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OrderService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const order_entity_1 = require("../entities/order.entity");
const order_item_entity_1 = require("../entities/order-item.entity");
const order_return_entity_1 = require("../entities/order-return.entity");
const order_return_item_entity_1 = require("../entities/order-return-item.entity");
const customer_entity_1 = require("../entities/customer.entity");
const product_entity_1 = require("../entities/product.entity");
const size_entity_1 = require("../entities/size.entity");
const stock_service_1 = require("../stock/stock.service");
let OrderService = class OrderService {
    orderRepository;
    dataSource;
    stockService;
    constructor(orderRepository, dataSource, stockService) {
        this.orderRepository = orderRepository;
        this.dataSource = dataSource;
        this.stockService = stockService;
    }
    async findAll(paginationDto) {
        const page = Math.max(Number.parseInt(paginationDto.page ?? "1", 10) || 1, 1);
        const limit = Math.max(Number.parseInt(paginationDto.limit ?? "10", 10) || 10, 1);
        const search = paginationDto.searchTerm;
        const baseQuery = this.orderRepository
            .createQueryBuilder("orders")
            .leftJoin("orders.customer", "customer");
        if (search) {
            const searchTerm = `%${search.toLowerCase()}%`;
            baseQuery.where("(LOWER(orders.order_code) LIKE :search OR LOWER(orders.status) LIKE :search OR LOWER(customer.first_name) LIKE :search OR LOWER(customer.last_name) LIKE :search OR LOWER(customer.email) LIKE :search OR LOWER(COALESCE(customer.phone, '')) LIKE :search)", { search: searchTerm });
        }
        baseQuery.orderBy("orders.created_at", "DESC");
        const total = await baseQuery.clone().getCount();
        const orderIdRows = await baseQuery
            .clone()
            .select("orders.id", "order_id")
            .skip((page - 1) * limit)
            .take(limit)
            .getRawMany();
        const orderIds = orderIdRows
            .map((row) => Number(row.order_id))
            .filter((id) => Number.isInteger(id));
        let data = [];
        if (orderIds.length > 0) {
            const orderIndex = new Map(orderIds.map((id, index) => [id, index]));
            const fetchedOrders = await this.orderRepository.find({
                where: { id: (0, typeorm_2.In)(orderIds) },
                relations: {
                    customer: true,
                    items: { product: true, size: true },
                    returns: { items: true },
                },
                order: { created_at: "DESC" },
            });
            data = fetchedOrders.sort((a, b) => (orderIndex.get(a.id) ?? Number.POSITIVE_INFINITY) -
                (orderIndex.get(b.id) ?? Number.POSITIVE_INFINITY));
        }
        const totalPages = total > 0 ? Math.ceil(total / limit) : 0;
        return {
            data,
            page,
            limit,
            total,
            previous: page > 1 ? page - 1 : null,
            next: page < totalPages ? page + 1 : null,
        };
    }
    async findOne(id) {
        return this.getOrderByIdOrFail(id);
    }
    async create(createOrderDto) {
        if (createOrderDto.items.length === 0) {
            throw new common_1.BadRequestException("Order must contain at least one item");
        }
        const queryRunner = this.dataSource.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction();
        try {
            const customer = await queryRunner.manager.findOne(customer_entity_1.Customer, {
                where: { id: createOrderDto.customerId },
            });
            if (!customer) {
                throw new common_1.NotFoundException(`Customer with ID ${createOrderDto.customerId} not found`);
            }
            const order = queryRunner.manager.create(order_entity_1.Order, {
                order_code: this.generateOrderCode(),
                customer,
                order_date: createOrderDto.orderDate
                    ? new Date(createOrderDto.orderDate)
                    : new Date(),
                status: "confirmed",
                total_amount: 0,
                refunded_amount: 0,
            });
            const savedOrder = await queryRunner.manager.save(order);
            let runningTotal = 0;
            for (const itemDto of createOrderDto.items) {
                const product = await queryRunner.manager.findOne(product_entity_1.Product, {
                    where: { id: itemDto.productId },
                });
                if (!product) {
                    throw new common_1.NotFoundException(`Product with ID ${itemDto.productId} not found`);
                }
                const size = await queryRunner.manager.findOne(size_entity_1.Size, {
                    where: { id: itemDto.sizeId },
                });
                if (!size) {
                    throw new common_1.NotFoundException(`Size with ID ${itemDto.sizeId} not found`);
                }
                await this.stockService.decreaseStock({
                    productId: product.id,
                    sizeId: size.id,
                    quantity: itemDto.quantity,
                    movementType: "SALE",
                    manager: queryRunner.manager,
                    referenceType: "ORDER",
                    referenceId: savedOrder.id,
                });
                const lineTotal = Number(itemDto.unitPrice) * itemDto.quantity;
                runningTotal += lineTotal;
                const orderItem = queryRunner.manager.create(order_item_entity_1.OrderItem, {
                    order: savedOrder,
                    product,
                    size,
                    size_id: size.id,
                    quantity: itemDto.quantity,
                    returned_quantity: 0,
                    unit_price: Number(itemDto.unitPrice),
                    line_total: lineTotal,
                });
                await queryRunner.manager.save(orderItem);
            }
            savedOrder.total_amount = runningTotal;
            await queryRunner.manager.save(savedOrder);
            await queryRunner.commitTransaction();
            return this.getOrderByIdOrFail(savedOrder.id);
        }
        catch (error) {
            await queryRunner.rollbackTransaction();
            throw error;
        }
        finally {
            await queryRunner.release();
        }
    }
    async updateStatus(id, updateOrderStatusDto) {
        const order = await this.getOrderByIdOrFail(id);
        order.status = updateOrderStatusDto.status;
        await this.orderRepository.save(order);
        return this.getOrderByIdOrFail(id);
    }
    async createReturn(orderId, createOrderReturnDto) {
        if (createOrderReturnDto.items.length === 0) {
            throw new common_1.BadRequestException("Return must contain at least one item");
        }
        const queryRunner = this.dataSource.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction();
        try {
            const order = await queryRunner.manager.findOne(order_entity_1.Order, {
                where: { id: orderId },
                relations: {
                    customer: true,
                    items: { product: true, size: true },
                    returns: { items: true },
                },
                lock: { mode: "pessimistic_write" },
            });
            if (!order) {
                throw new common_1.NotFoundException(`Order with ID ${orderId} not found`);
            }
            const returnEntity = queryRunner.manager.create(order_return_entity_1.OrderReturn, {
                order,
                reason: createOrderReturnDto.reason ?? null,
                total_amount: 0,
            });
            const savedReturn = await queryRunner.manager.save(returnEntity);
            let totalReturnAmount = 0;
            const persistedReturnItems = [];
            for (const itemDto of createOrderReturnDto.items) {
                const orderItem = order.items.find((item) => item.id === itemDto.orderItemId);
                if (!orderItem) {
                    throw new common_1.NotFoundException(`Order item with ID ${itemDto.orderItemId} not found in order ${orderId}`);
                }
                const remainingQuantity = orderItem.quantity - orderItem.returned_quantity;
                if (itemDto.quantity > remainingQuantity) {
                    throw new common_1.BadRequestException(`Cannot return ${itemDto.quantity} items. Only ${remainingQuantity} remaining for order item ${orderItem.id}`);
                }
                orderItem.returned_quantity += itemDto.quantity;
                await queryRunner.manager.save(orderItem);
                const returnAmount = Number(orderItem.unit_price) * itemDto.quantity;
                totalReturnAmount += returnAmount;
                await this.stockService.increaseStock({
                    productId: orderItem.product.id,
                    sizeId: orderItem.size.id,
                    quantity: itemDto.quantity,
                    movementType: "RETURN",
                    manager: queryRunner.manager,
                    referenceType: "ORDER_RETURN",
                    referenceId: savedReturn.id,
                });
                const returnItem = queryRunner.manager.create(order_return_item_entity_1.OrderReturnItem, {
                    orderReturn: savedReturn,
                    orderItem,
                    quantity: itemDto.quantity,
                    amount: returnAmount,
                });
                persistedReturnItems.push(await queryRunner.manager.save(returnItem));
            }
            if (persistedReturnItems.length === 0) {
                throw new common_1.BadRequestException("No return items were processed");
            }
            savedReturn.total_amount = totalReturnAmount;
            savedReturn.items = persistedReturnItems;
            await queryRunner.manager.save(savedReturn);
            const grossTotal = order.items.reduce((sum, item) => sum + Number(item.line_total), 0);
            const refundedAmount = order.items.reduce((sum, item) => sum + Number(item.unit_price) * item.returned_quantity, 0);
            order.refunded_amount = refundedAmount;
            order.total_amount = grossTotal - refundedAmount;
            await queryRunner.manager.save(order);
            await queryRunner.commitTransaction();
            return this.getOrderByIdOrFail(order.id);
        }
        catch (error) {
            await queryRunner.rollbackTransaction();
            throw error;
        }
        finally {
            await queryRunner.release();
        }
    }
    async remove(id) {
        const result = await this.orderRepository.delete(id);
        if (result.affected === 0) {
            throw new common_1.NotFoundException(`Order with ID ${id} not found`);
        }
    }
    async getOrderByIdOrFail(id) {
        const order = await this.orderRepository.findOne({
            where: { id },
            relations: {
                customer: true,
                items: { product: true, size: true },
                returns: { items: true },
            },
        });
        if (!order) {
            throw new common_1.NotFoundException(`Order with ID ${id} not found`);
        }
        return order;
    }
    generateOrderCode() {
        const now = new Date();
        const datePart = `${now.getFullYear()}${(now.getMonth() + 1)
            .toString()
            .padStart(2, "0")}${now
            .getDate()
            .toString()
            .padStart(2, "0")}`;
        return `ORD-${datePart}-${now.getTime()}`;
    }
};
exports.OrderService = OrderService;
exports.OrderService = OrderService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(order_entity_1.Order)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.DataSource,
        stock_service_1.StockService])
], OrderService);
//# sourceMappingURL=order.service.js.map