import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport";
import { settings } from "@pkg/settings";
import type { Err } from "@pkg/result";
import winston from "winston";
import util from "util";

process.on("warning", (warning) => {
    const msg = String(warning?.message || "");
    const name = String((warning as any)?.name || "");

    if (
        name === "TimeoutNegativeWarning" ||
        msg.includes("TimeoutNegativeWarning") ||
        msg.includes("Timeout duration was set to 1")
    ) {
        return;
    }

    console.warn(warning);
});

const levels = {
    error: 0,
    warn: 1,
    info: 2,
    http: 3,
    debug: 4,
} as const;

const colors = {
    error: "red",
    warn: "yellow",
    info: "green",
    http: "magenta",
    debug: "white",
};

const level = () => {
    const envLevel = process.env.LOG_LEVEL?.toLowerCase();
    if (envLevel && Object.prototype.hasOwnProperty.call(levels, envLevel)) {
        return envLevel as keyof typeof levels;
    }
    return settings.isDevelopment ? "debug" : "warn";
};

const consoleFormat = winston.format.combine(
    winston.format.errors({ stack: true }),
    winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss:ms" }),
    winston.format.colorize({ all: true }),
    winston.format.printf((info) => {
        const { level, message, timestamp } = info;

        const extra = Object.fromEntries(
            Object.entries(info).filter(
                ([key]) => key !== "level" && key !== "message" && key !== "timestamp",
            ),
        );

        const formattedMessage =
            message instanceof Error
                ? message.stack || message.message
                : typeof message === "object"
                  ? util.inspect(message, { depth: null, colors: true })
                  : String(message);

        const formattedExtra =
            Object.keys(extra).length > 0
                ? `\n${util.inspect(extra, { depth: null, colors: true })}`
                : "";

        return `[${level}] ${timestamp}: ${formattedMessage}${formattedExtra}`;
    }),
);

winston.addColors(colors);

const logger = winston.createLogger({
    level: level(),
    levels,
    format: consoleFormat,
    transports: [
        new winston.transports.Console({ format: consoleFormat }),
        new OpenTelemetryTransportV3(),
    ],
    exceptionHandlers: [new winston.transports.Console({ format: consoleFormat })],
    rejectionHandlers: [new winston.transports.Console({ format: consoleFormat })],
});

const stream = { write: (message: string) => logger.http(message.trim()) };

type LogLevel = keyof typeof levels;
type ErrorKind = "validation" | "auth" | "db" | "external" | "unknown";
type FlowCtxLike = {
    flowId: string;
    userId?: string;
    sessionId?: string;
};

const REDACTED_KEYS = new Set([
    "password",
    "code",
    "secret",
    "token",
    "verificationtoken",
    "backupcodes",
    "authorization",
    "headers",
    "hash",
]);

function sanitizeMeta(input: Record<string, unknown>) {
    const sanitized = Object.fromEntries(
        Object.entries(input).map(([key, value]) => {
            if (value === undefined) {
                return [key, undefined];
            }
            const lowered = key.toLowerCase();
            if (REDACTED_KEYS.has(lowered)) {
                return [key, "[REDACTED]"];
            }
            return [key, value];
        }),
    );

    return Object.fromEntries(
        Object.entries(sanitized).filter(([, value]) => value !== undefined),
    );
}

function classifyError(error: unknown): ErrorKind {
    if (!error) return "unknown";
    if (typeof error === "object" && error && "code" in error) {
        const code = String((error as { code?: unknown }).code ?? "").toUpperCase();
        if (code.includes("AUTH") || code.includes("UNAUTHORIZED")) return "auth";
        if (code.includes("VALIDATION") || code.includes("INVALID")) return "validation";
        if (code.includes("DB") || code.includes("DATABASE")) return "db";
    }
    return "unknown";
}

function errorMessage(error: unknown) {
    if (error instanceof Error) return error.message;
    if (typeof error === "string") return error;
    if (error && typeof error === "object" && "message" in error) {
        return String((error as { message?: unknown }).message ?? "Unknown error");
    }
    return "Unknown error";
}

function formatErrorDetail(error: unknown): string {
    if (error instanceof Error) {
        return error.stack || error.message;
    }
    if (typeof error === "string") {
        return error;
    }
    if (!error) {
        return "Unknown error";
    }
    if (typeof error === "object") {
        const candidate = error as {
            code?: unknown;
            message?: unknown;
            description?: unknown;
            detail?: unknown;
            error?: unknown;
            stack?: unknown;
        };

        const parts = [
            candidate.code ? `code=${String(candidate.code)}` : null,
            candidate.message ? `message=${String(candidate.message)}` : null,
            candidate.description
                ? `description=${String(candidate.description)}`
                : null,
            candidate.detail
                ? `detail=${
                      typeof candidate.detail === "string"
                          ? candidate.detail
                          : util.inspect(candidate.detail, {
                                depth: null,
                                colors: false,
                            })
                  }`
                : null,
            candidate.error
                ? `error=${
                      typeof candidate.error === "string"
                          ? candidate.error
                          : util.inspect(candidate.error, {
                                depth: null,
                                colors: false,
                            })
                  }`
                : null,
            candidate.stack ? String(candidate.stack) : null,
        ].filter(Boolean);

        return parts.length > 0
            ? parts.join(" | ")
            : util.inspect(error, { depth: null, colors: false });
    }

    return String(error);
}

function writeLog(level: LogLevel, message: string, payload: Record<string, unknown>) {
    switch (level) {
        case "error":
            logger.error(message, payload);
            return;
        case "warn":
            logger.warn(message, payload);
            return;
        case "debug":
            logger.debug(message, payload);
            return;
        case "http":
            logger.http(message, payload);
            return;
        default:
            logger.info(message, payload);
    }
}

function logDomainEvent({
    level = "info",
    event,
    fctx,
    durationMs,
    error,
    retryable,
    meta,
}: {
    level?: LogLevel;
    event: string;
    fctx: FlowCtxLike;
    durationMs?: number;
    error?: unknown;
    retryable?: boolean;
    meta?: Record<string, unknown>;
}) {
    const payload: Record<string, unknown> = {
        event,
        flowId: fctx.flowId,
        userId: fctx.userId,
        sessionId: fctx.sessionId,
    };

    if (durationMs !== undefined) payload.duration_ms = durationMs;
    if (retryable !== undefined) payload.retryable = retryable;
    if (error !== undefined) {
        payload.error_kind = classifyError(error);
        payload.error_message = errorMessage(error);
        if (error && typeof error === "object" && "code" in error) {
            payload.error_code = String(
                (error as { code?: unknown }).code ?? "UNKNOWN",
            );
        }
    }
    if (meta) {
        Object.assign(payload, sanitizeMeta(meta));
    }

    writeLog(level, event, payload);
}

function getError(payload: Err, error?: any) {
    logger.error(JSON.stringify({ payload, error }, null, 2));
    return {
        code: payload.code,
        message: payload.message,
        description: payload.description,
        detail: payload.detail,
        error: error instanceof Error ? error.message : error,
        actionable: payload.actionable,
    } as Err;
}

export { getError, logDomainEvent, logger, stream };
export { formatErrorDetail };
