import { MessageBroker } from "@3kles/3kles-amqpbroker";
import { IIonMessage, IIonRoute } from "../models";
import { IONConnector } from "../connectors";

export class IonBroker {

    private prefix: string;
    private exchange: string;
    private queue: string;

    constructor(
        private broker: MessageBroker,
        private ionConnector: IONConnector,
        private ionRoute: IIonRoute,
        option?: { exchange?: string, prefix?: string, queue?: string }) {

        this.prefix = option?.prefix || process.env.PREFIX;
        this.exchange = this.prefix ? `${this.prefix}.${(option?.exchange || ionConnector.context)}` : (option?.exchange || ionConnector.context);
        this.queue = (this.prefix && option?.queue) ? `${this.prefix}.${option?.queue}` : (option?.queue || '');
    }

    public async listen(): Promise<void> {
        await this.broker.subscribeExchange(this.queue, this.exchange, this.ionRoute.routingKey, 'direct', async (data, ack) => {
            if (data) {
                try {
                    const ionMessage = this.parseMessage(data.content.toString());
                    if (!this.ionRoute.routes[ionMessage.key]?.option?.path) {
                        throw new Error(`[ION Service]: key ${ionMessage.key} not exist`);
                    }

                    const response = await this.executeRequest(ionMessage);
                    await this.sendResponse(data, response);
                } catch (err) {
                    console.error(err);

                    await this.sendErrorResponse(data, err);

                }
                ack();
            }

        }, { autoDelete: true, durable: false }, { durable: false });
    }

    protected parseMessage(message: string): IIonMessage {
        return JSON.parse(message);
    }

    protected executeRequest(ionMessage: IIonMessage): Promise<{ statusCode: number; headers: any; body: any; }> {
        return this.ionConnector.executeRequest(this.ionConnector.buildRequest({
            path: this.ionRoute.routes[ionMessage.key]?.option.path,
            method: this.ionRoute.routes[ionMessage.key]?.method,
            ...(ionMessage.query && { query: ionMessage.query }),
            ...(ionMessage.params && { params: ionMessage.params }),
            ...(ionMessage.data && { data: JSON.stringify(ionMessage.data) }),
        }));
    }

    protected async sendResponse(data: { properties }, response: any): Promise<void> {
        if (data.properties?.replyTo) {
            await this.broker.channel.sendToQueue(data.properties.replyTo,
                Buffer.from(JSON.stringify(response)),
                {
                    correlationId: data.properties.correlationId
                }
            );
        }
    }

    protected async sendErrorResponse(data: { properties }, error: any): Promise<void> {
        if (data.properties?.replyTo) {
            await this.broker.channel.sendToQueue(data.properties.replyTo,
                Buffer.from(JSON.stringify({ error: (error.message || 'Error with ION') })),
                {
                    correlationId: data.properties.correlationId
                }
            );
        }
    }
}
