import { ensureFontLoaded, getPrimaryFamily, loadImage } from "./avatar.utils";

type RenderBaseAvatarParams = {
    canvas: HTMLCanvasElement;
    selectedImage: string;
    customBg: string | null;
    customImage: string | null;
    text: string;
    textColor: string;
    font: string;
    fontSize: number;
    textPosition: { x: number; y: number };
    useOutline: boolean;
    outlineColor: string;
    outlineWidth: number;
    useShadow: boolean;
    shadowColor: string;
    shadowBlur: number;
    shadowOffsetX: number;
    shadowOffsetY: number;
    useGradient: boolean;
    gradientType: "linear" | "radial";
    gradColor1: string;
    gradColor2: string;
    gradAngle: number;
    gradRadius: number;
    textRotation: number;
};

type RenderOverlayParams = {
    canvas: HTMLCanvasElement;
    text: string;
    textPosition: { x: number; y: number };
    textRotation: number;
    gizmoDist: number;
    gizmoRadius: number;
};

export const getCanvasXY = (
    canvas: HTMLCanvasElement,
    e: PointerEvent | React.PointerEvent<HTMLCanvasElement>
) => {
    const rect = canvas.getBoundingClientRect();
    const scaleX = canvas.width / rect.width;
    const scaleY = canvas.height / rect.height;

    return {
        x: ( e.clientX - rect.left ) * scaleX,
        y: ( e.clientY - rect.top ) * scaleY,
    };
};

const coverDraw = (
    ctx: CanvasRenderingContext2D,
    img: HTMLImageElement,
    width: number,
    height: number
) => {
    const iw = img.width;
    const ih = img.height;
    const scale = Math.max( width / iw, height / ih );
    const sw = iw * scale;
    const sh = ih * scale;
    const dx = ( width - sw ) / 2;
    const dy = ( height - sh ) / 2;
    ctx.drawImage( img, dx, dy, sw, sh );
};

const drawTextOnly = (
    ctx: CanvasRenderingContext2D,
    params: Omit<RenderBaseAvatarParams, "canvas" | "selectedImage" | "customBg" | "customImage">
) => {
    const {
        text,
        textColor,
        font,
        fontSize,
        textPosition,
        useOutline,
        outlineColor,
        outlineWidth,
        useShadow,
        shadowColor,
        shadowBlur,
        shadowOffsetX,
        shadowOffsetY,
        useGradient,
        gradientType,
        gradColor1,
        gradColor2,
        gradAngle,
        gradRadius,
        textRotation,
    } = params;

    if ( !text ) return;

    if ( useShadow ) {
        ctx.shadowColor = shadowColor;
        ctx.shadowBlur = shadowBlur;
        ctx.shadowOffsetX = shadowOffsetX;
        ctx.shadowOffsetY = shadowOffsetY;
    } else {
        ctx.shadowColor = "transparent";
        ctx.shadowBlur = 0;
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
    }

    ctx.save();
    ctx.translate( textPosition.x, textPosition.y );
    ctx.rotate( ( ( textRotation % 360 ) * Math.PI ) / 180 );
    ctx.font = `bold ${ fontSize }px ${ font }`;
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

    if ( useOutline && outlineWidth > 0 ) {
        ctx.lineWidth = outlineWidth;
        ctx.strokeStyle = outlineColor;
        ctx.strokeText( text, 0, 0 );
    }

    let fill: string | CanvasGradient = textColor;

    if ( useGradient ) {
        const m = ctx.measureText( text );
        const w = Math.max( 1, m.width );
        const h = Math.max( 1, fontSize );

        if ( gradientType === "linear" ) {
            const ang = ( ( gradAngle % 360 ) * Math.PI ) / 180;
            const half = Math.max( w, h ) * 0.75;
            const x0 = -Math.cos( ang ) * half;
            const y0 = -Math.sin( ang ) * half;
            const x1 = Math.cos( ang ) * half;
            const y1 = Math.sin( ang ) * half;
            const g = ctx.createLinearGradient( x0, y0, x1, y1 );
            g.addColorStop( 0, gradColor1 );
            g.addColorStop( 1, gradColor2 );
            fill = g;
        } else {
            const g = ctx.createRadialGradient( 0, 0, 0, 0, 0, Math.max( 10, gradRadius ) );
            g.addColorStop( 0, gradColor1 );
            g.addColorStop( 1, gradColor2 );
            fill = g;
        }
    }

    ctx.fillStyle = fill;
    ctx.fillText( text, 0, 0 );

    ctx.restore();
};

export const renderBaseAvatar = async ( {
    canvas,
    selectedImage,
    customBg,
    customImage,
    ...rest
}: RenderBaseAvatarParams ) => {
    const ctx = canvas.getContext( "2d" );
    if ( !ctx ) return;

    canvas.width = 800;
    canvas.height = 800;
    ctx.clearRect( 0, 0, canvas.width, canvas.height );

    await ensureFontLoaded( getPrimaryFamily( rest.font ), rest.fontSize );

    const isLogoT = selectedImage === "Logo_T.png";

    if ( isLogoT && customBg ) {
        const bg = await loadImage( customBg );
        coverDraw( ctx, bg, 800, 800 );

        const logo = await loadImage( "Logo_T.png" );
        ctx.drawImage( logo, 0, 0, 800, 800 );

        drawTextOnly( ctx, rest );
        return;
    }

    const baseSrc = customImage || selectedImage;
    const base = await loadImage( baseSrc );
    ctx.drawImage( base, 0, 0, 800, 800 );

    drawTextOnly( ctx, rest );
};

export const renderOverlayGizmo = ( {
    canvas,
    text,
    textPosition,
    textRotation,
    gizmoDist,
    gizmoRadius,
}: RenderOverlayParams ) => {
    const ctx = canvas.getContext( "2d" );
    if ( !ctx ) return;

    canvas.width = 800;
    canvas.height = 800;
    ctx.clearRect( 0, 0, canvas.width, canvas.height );

    if ( !text ) return;

    ctx.save();
    ctx.translate( textPosition.x, textPosition.y );
    ctx.rotate( ( ( textRotation % 360 ) * Math.PI ) / 180 );

    ctx.shadowColor = "transparent";
    ctx.shadowBlur = 0;
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = 0;

    const gx = gizmoDist;
    const gy = 0;

    ctx.beginPath();
    ctx.moveTo( 0, 0 );
    ctx.lineTo( gx, gy );
    ctx.lineWidth = 1.5;
    ctx.strokeStyle = "rgba(255,255,255,0.35)";
    ctx.stroke();

    ctx.beginPath();
    ctx.arc( gx, gy, gizmoRadius, 0, Math.PI * 2 );
    ctx.fillStyle = "rgba(99,102,241,0.9)";
    ctx.fill();
    ctx.lineWidth = 2;
    ctx.strokeStyle = "rgba(255,255,255,0.6)";
    ctx.stroke();

    ctx.restore();
};