import {
    BadRequestException,
    ConflictException,
    Injectable,
    InternalServerErrorException,
    NotFoundException,
} from "@nestjs/common";
import { PrismaService } from "../../prisma/prisma.service";

import { handlePrismaError } from "src/common/lib/handlePrismaError";
import { ConfigService } from "@nestjs/config";
import {
    formatNoDataResponse,
    formatPaginatedResponse,
    getPaginationOptions,
} from "src/common/lib/pagination-helper";
import { CreateClientDto } from "./dto/create-client.dto";
import { UpdateClientDto } from "./dto/update-client.dto";
import { ClientSelect } from "src/selects/client";

@Injectable()
export class ClientsService {
    constructor(
        private prisma: PrismaService,
        private readonly configService: ConfigService,
    ) { }


    async create(createClientDto: CreateClientDto, user: any) {
        const { email, phone } = createClientDto;
        let warehouseId = createClientDto.warehouseId;

        // EMAIL UNIQUE CHECK
        if (email) {
            const existingEmail = await this.prisma.clients.findUnique({
                where: { email },
            });

            if (existingEmail) {
                throw new ConflictException("Email already exists");
            }
        }

        // PHONE UNIQUE CHECK
        if (phone) {
            const existingPhone = await this.prisma.clients.findUnique({
                where: { phone },
            });

            if (existingPhone) {
                throw new ConflictException("Phone number already exists");
            }
        }

        if (user.role !== 'ADMIN') {
            const getUser = await this.prisma.users.findUnique({
                where: { id: user.sub },
                select: { warehouseId: true },
            });

            if (!getUser?.warehouseId) {
                throw new NotFoundException('User warehouse not found');
            }

            warehouseId = getUser.warehouseId;
        }

        if (!warehouseId) {
            throw new BadRequestException('WarehouseId is required');
        }

        return this.prisma.clients.create({
            data: {
                ...createClientDto,
                warehouseId,
            },
            select: ClientSelect,
        });
    }

    async findAll(page: number, perPage: number, query: string | undefined, warehouseId?: string, user?: any,) {
        try {
            const { skip, take } = getPaginationOptions({ page, perPage });
            const where: any = {};

            if (query) {
                where.OR = [
                    { name: { contains: query } },
                    { phone: { contains: query } },
                    { email: { contains: query } }
                ];
            }

            if (warehouseId && warehouseId.trim() !== '') {
                where.warehouseId = warehouseId;
            } else if (user && user.role !== 'ADMIN') {
                const getUser = await this.prisma.users.findFirst({
                    where: {
                        id: user.sub
                    }
                })
                where.warehouseId = getUser?.warehouseId;
            }


            const [items, total] = await this.prisma.$transaction([
                this.prisma.clients.findMany({
                    where,
                    select: ClientSelect,
                    skip,
                    take,
                    orderBy: { createdAt: "desc" },
                }),
                this.prisma.clients.count({ where }),
            ]);

            const clientIds = items.map((c) => c.id);

            const grouped = clientIds.length
                ? await this.prisma.cylinders.groupBy({
                    by: ['clientId'],
                    where: { clientId: { in: clientIds }, is_client_owned: 1 },
                    _count: { _all: true },
                })
                : [];

            const itemsWithCounts = items.map((c) => ({
                ...c,
                totalCylinders:
                    grouped.find((g) => g.clientId === c.id)?._count._all || 0,
            }));

            return formatPaginatedResponse(itemsWithCounts, total, page, perPage);
        } catch (error) {
            handlePrismaError(error, "Fetching paginated Clients");
        }
    }

    /* ----------------------------------------------------
         GET ONE CLIENT
    ----------------------------------------------------*/
    async findOne(id: any) {
        const client = await this.prisma.clients.findUnique({
            where: { id },
            select: ClientSelect,
        });

        if (!client) {
            throw new NotFoundException(`Client not found`);
        }

        return client;
    }

    /* ----------------------------------------------------
         UPDATE CLIENT → UNIQUE EMAIL + PHONE CHECK
    ----------------------------------------------------*/
    async update(id: string, updateClientDto: UpdateClientDto, user?: any) {
        await this.findOne(id);

        let warehouseId = updateClientDto.warehouseId;

        // EMAIL UNIQUE CHECK
        if (updateClientDto.email) {
            const existingEmail = await this.prisma.clients.findFirst({
                where: {
                    email: updateClientDto.email,
                    NOT: { id },
                },
            });

            if (existingEmail) {
                throw new ConflictException("Email already exists");
            }
        }

        // PHONE UNIQUE CHECK
        if (updateClientDto.phone) {
            const existingPhone = await this.prisma.clients.findFirst({
                where: {
                    phone: updateClientDto.phone,
                    NOT: { id },
                },
            });

            if (existingPhone) {
                throw new ConflictException("Phone number already exists");
            }
        }

        if (user?.role !== 'ADMIN') {
            const getUser = await this.prisma.users.findUnique({
                where: { id: user.sub },
                select: { warehouseId: true },
            });

            if (!getUser?.warehouseId) {
                throw new NotFoundException('User warehouse not found');
            }

            warehouseId = getUser.warehouseId;
        }

        if (!warehouseId) {
            throw new BadRequestException('WarehouseId is required');
        }

        return this.prisma.clients.update({
            where: { id },
            data: {
                ...updateClientDto,
                warehouseId,
            },
            select: ClientSelect,
        });
    }

    /* ----------------------------------------------------
         DELETE CLIENT
    ----------------------------------------------------*/
    async remove(id: string) {
        await this.findOne(id);
        return this.prisma.clients.delete({
            where: { id },
            select: { id: true },
        });
    }

    /* ----------------------------------------------------
         SEARCH CLIENT
    ----------------------------------------------------*/
    async searchClient(query: string, page: number, perPage: number) {
        const cleanedQuery = query?.trim();
        if (!cleanedQuery) throw new BadRequestException("Search query is required");

        const { skip, take } = getPaginationOptions({ page, perPage });

        const where = {
            OR: [
                { name: { contains: cleanedQuery } },
                { email: { contains: cleanedQuery } },
                { phone: { contains: cleanedQuery } },
                { gst: { contains: cleanedQuery } },
                { representative: { contains: cleanedQuery } },
                { representativephoneno: { contains: cleanedQuery } },
            ],
        };

        const [items, total] = await this.prisma.$transaction([
            this.prisma.clients.findMany({
                where,
                select: ClientSelect,
                skip,
                take,
                orderBy: { createdAt: "desc" },
            }),
            this.prisma.clients.count({ where }),
        ]);

        if (total === 0) return formatNoDataResponse("Client Not Found.");
        return formatPaginatedResponse(items, total, page, perPage);
    }

    async getAllClients(page: number, perPage: number, search?: string, warehouseId?: string) {
        try {
            const { skip, take } = getPaginationOptions({ page, perPage });
            const where: any = {};

            if (warehouseId) {
                where.warehouseId = warehouseId;
            }

            if (search) {
                search = search.trim();
                where.name = { contains: search };
            }

            const [items, total] = await this.prisma.$transaction([
                this.prisma.clients.findMany({
                    where,
                    select: {
                        id: true,
                        name: true,
                    },
                    skip,
                    take,
                    orderBy: { name: "asc" },
                }),
                this.prisma.clients.count({ where }),
            ]);

            return formatPaginatedResponse(items, total, page, perPage);
        } catch (error) {
            handlePrismaError(error, "Fetching paginated Clients");
        }
    }
}
