import Docker from "dockerode";

const docker = new Docker();

/**
 * Listar todos los contenedores.
 */
export const listContainers = async () => {
    return docker.listContainers({ all: true, filters: { ancestor: ["txbot:latest"] } });
};

export const existsContainer = async (organizationId: string): Promise<boolean> => {
    const contenedores = await listContainers();
    const contenedoresNombres = Array.from(contenedores, (item) =>
        item.Names[0].split("_bot")[0].substring(1)
    );

    if (contenedoresNombres.includes(organizationId)) {
        return true;
    }
    return false;
};

export const getOrganizationBotPort = async (organizationId: string): Promise<number> => {
    const contenedores = await listContainers();

    for (const contenedor of contenedores) {
        if (contenedor.Names.includes(organizationId)) {
            const port = contenedor.Ports[0].PublicPort;
            return port;
        }
    }
    return -1;
};

export const getAvailablePort = async (): Promise<number> => {
    const contenedores = await listContainers();

    let defaultPort = process.env.DEFAULT_BOT_START_PORT
        ? parseInt(process.env.DEFAULT_BOT_START_PORT)
        : -1;

    if (contenedores.length == 0) {
        return defaultPort;
    }

    contenedores.sort((a, b) => b.Created - a.Created);

    //Obtengo el contenedor mas reciente.
    const lastContainerCreated = contenedores.shift();

    if (!lastContainerCreated) {
        return defaultPort;
    }

    const lastPort = lastContainerCreated.Ports;

    if (!lastPort) {
        return defaultPort;
    }

    if (lastPort.length == 0) {
        return defaultPort;
    }

    return lastPort[0].PublicPort + 1;
};

/**
 * Iniciar un contenedor por ID.
 */
export const startContainer = async (containerId: string) => {
    const container = docker.getContainer(containerId);
    await container.start();
};

/**
 * Detener un contenedor por ID.
 */
export const stopContainer = async (containerId: string) => {
    const container = docker.getContainer(containerId);
    await container.stop();
};

export const createContainer = async (
    port: number,
    name: string,
    image: string
): Promise<string> => {
    try {
        // Crear un contenedor con las configuraciones especificadas
        const container = await docker.createContainer({
            Image: image,
            name,
            Env: [`PORT=${port}`],
            ExposedPorts: {
                [`${port}/tcp`]: {}, // Define el puerto expuesto
            },
            HostConfig: {
                PortBindings: {
                    [`${port}/tcp`]: [{ HostPort: `${port}` }], // Mapea el puerto entre el host y el contenedor
                },
            },
            NetworkingConfig: {
                EndpointsConfig: {
                    "bot-admin-api_tx-net": {}, // importante: debe coincidir con el nombre exacto de la red
                },
            },
        });

        // Iniciar el contenedor creado
        await container.start();

        // Devolver el ID del contenedor como resultado
        return container.id;
    } catch (error: any) {
        throw new Error(`Error al crear el contenedor: ${error.message}`);
    }
};

export const removeContainer = async (containerId: string): Promise<void> => {
    try {
        // Obtiene el contenedor por su ID
        const container = docker.getContainer(containerId);

        // Verifica si el contenedor existe antes de intentar eliminarlo
        const containerInfo = await container.inspect();
        if (containerInfo.State.Running) {
            // Si el contenedor está corriendo, debe detenerse primero
            await container.stop();
        }

        // Elimina el contenedor
        await container.remove();
    } catch (error: any) {
        throw new Error(`Error al eliminar el contenedor: ${error.message}`);
    }
};

export const getContainerByOrganization = async (
    organizationId: string
): Promise<Docker.ContainerInfo> => {
    const contenedores = await listContainers();

    for (const contenedor of contenedores) {
        if (contenedor.Names.some((item) => item.includes(organizationId))) {
            return contenedor;
        }
    }

    throw new Error("Container not found");
};
