use super::*;

pub(crate) fn apply_style_layers(
    image: &mut RgbaImage,
    spec: AvatarSpec,
    style: AvatarStyleOptions,
    identity: &AvatarIdentity,
) {
    if !style.has_extra_layers() {
        return;
    }

    let accent = style_accent_color(style.color, identity);
    if style.color != AvatarColor::Default {
        draw_style_color_layer(image, spec, accent);
    }
    draw_expression_layer(image, spec, style.kind, style.expression, accent);
    draw_accessory_layer(image, spec, style.kind, style.accessory, accent);
    apply_shape_layer(image, spec, style.shape, accent);
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct AvatarLayerAnchors {
    left_eye: (f32, f32),
    right_eye: (f32, f32),
    mouth: (f32, f32),
    top: f32,
    neck: f32,
    face_width: f32,
    eye_radius: f32,
}

impl AvatarLayerAnchors {
    fn point(self, spec: AvatarSpec, point: (f32, f32)) -> (i32, i32) {
        (
            (point.0 * spec.width as f32).round() as i32,
            (point.1 * spec.height as f32).round() as i32,
        )
    }

    fn y(self, spec: AvatarSpec, value: f32) -> i32 {
        (value * spec.height as f32).round() as i32
    }

    fn span(self, spec: AvatarSpec, value: f32) -> i32 {
        (value * spec.width.min(spec.height) as f32).round() as i32
    }
}

pub(crate) fn avatar_layer_anchors(kind: AvatarKind) -> Option<AvatarLayerAnchors> {
    let anchors = match kind {
        AvatarKind::Cat => AvatarLayerAnchors {
            left_eye: (0.40, 0.50),
            right_eye: (0.60, 0.50),
            mouth: (0.50, 0.62),
            top: 0.28,
            neck: 0.72,
            face_width: 0.58,
            eye_radius: 0.055,
        },
        AvatarKind::Dog => AvatarLayerAnchors {
            left_eye: (0.40, 0.46),
            right_eye: (0.60, 0.46),
            mouth: (0.50, 0.61),
            top: 0.24,
            neck: 0.72,
            face_width: 0.62,
            eye_radius: 0.055,
        },
        AvatarKind::Robot => AvatarLayerAnchors {
            left_eye: (0.38, 0.45),
            right_eye: (0.62, 0.45),
            mouth: (0.50, 0.60),
            top: 0.25,
            neck: 0.72,
            face_width: 0.54,
            eye_radius: 0.060,
        },
        AvatarKind::Fox => AvatarLayerAnchors {
            left_eye: (0.40, 0.48),
            right_eye: (0.60, 0.48),
            mouth: (0.50, 0.62),
            top: 0.25,
            neck: 0.72,
            face_width: 0.60,
            eye_radius: 0.050,
        },
        AvatarKind::Alien => AvatarLayerAnchors {
            left_eye: (0.39, 0.48),
            right_eye: (0.61, 0.48),
            mouth: (0.50, 0.63),
            top: 0.25,
            neck: 0.72,
            face_width: 0.58,
            eye_radius: 0.070,
        },
        AvatarKind::Monster => AvatarLayerAnchors {
            left_eye: (0.45, 0.52),
            right_eye: (0.55, 0.52),
            mouth: (0.50, 0.66),
            top: 0.27,
            neck: 0.74,
            face_width: 0.62,
            eye_radius: 0.045,
        },
        AvatarKind::Ghost => AvatarLayerAnchors {
            left_eye: (0.44, 0.50),
            right_eye: (0.56, 0.50),
            mouth: (0.50, 0.61),
            top: 0.27,
            neck: 0.72,
            face_width: 0.54,
            eye_radius: 0.045,
        },
        AvatarKind::Slime => AvatarLayerAnchors {
            left_eye: (0.40, 0.48),
            right_eye: (0.60, 0.48),
            mouth: (0.50, 0.61),
            top: 0.32,
            neck: 0.73,
            face_width: 0.58,
            eye_radius: 0.055,
        },
        AvatarKind::Bird => AvatarLayerAnchors {
            left_eye: (0.445, 0.51),
            right_eye: (0.555, 0.51),
            mouth: (0.50, 0.61),
            top: 0.31,
            neck: 0.70,
            face_width: 0.50,
            eye_radius: 0.035,
        },
        AvatarKind::Wizard => AvatarLayerAnchors {
            left_eye: (0.455, 0.55),
            right_eye: (0.545, 0.55),
            mouth: (0.50, 0.67),
            top: 0.36,
            neck: 0.73,
            face_width: 0.44,
            eye_radius: 0.040,
        },
        AvatarKind::Skull => AvatarLayerAnchors {
            left_eye: (0.40, 0.45),
            right_eye: (0.60, 0.45),
            mouth: (0.50, 0.63),
            top: 0.24,
            neck: 0.72,
            face_width: 0.55,
            eye_radius: 0.065,
        },
        AvatarKind::Frog => AvatarLayerAnchors {
            left_eye: (0.37, 0.39),
            right_eye: (0.63, 0.39),
            mouth: (0.50, 0.62),
            top: 0.25,
            neck: 0.72,
            face_width: 0.64,
            eye_radius: 0.070,
        },
        AvatarKind::Panda => AvatarLayerAnchors {
            left_eye: (0.41, 0.53),
            right_eye: (0.59, 0.53),
            mouth: (0.50, 0.62),
            top: 0.28,
            neck: 0.72,
            face_width: 0.62,
            eye_radius: 0.040,
        },
        AvatarKind::Octopus => AvatarLayerAnchors {
            left_eye: (0.42, 0.52),
            right_eye: (0.58, 0.52),
            mouth: (0.50, 0.65),
            top: 0.28,
            neck: 0.70,
            face_width: 0.58,
            eye_radius: 0.045,
        },
        AvatarKind::Knight => AvatarLayerAnchors {
            left_eye: (0.40, 0.45),
            right_eye: (0.60, 0.45),
            mouth: (0.50, 0.60),
            top: 0.22,
            neck: 0.72,
            face_width: 0.58,
            eye_radius: 0.050,
        },
        AvatarKind::Bear => AvatarLayerAnchors {
            left_eye: (0.40, 0.48),
            right_eye: (0.60, 0.48),
            mouth: (0.50, 0.63),
            top: 0.25,
            neck: 0.73,
            face_width: 0.62,
            eye_radius: 0.055,
        },
        AvatarKind::Penguin => AvatarLayerAnchors {
            left_eye: (0.40, 0.45),
            right_eye: (0.60, 0.45),
            mouth: (0.50, 0.57),
            top: 0.20,
            neck: 0.74,
            face_width: 0.58,
            eye_radius: 0.045,
        },
        AvatarKind::Dragon => AvatarLayerAnchors {
            left_eye: (0.40, 0.48),
            right_eye: (0.60, 0.48),
            mouth: (0.50, 0.63),
            top: 0.23,
            neck: 0.73,
            face_width: 0.62,
            eye_radius: 0.052,
        },
        AvatarKind::Ninja => AvatarLayerAnchors {
            left_eye: (0.40, 0.48),
            right_eye: (0.60, 0.48),
            mouth: (0.50, 0.64),
            top: 0.24,
            neck: 0.73,
            face_width: 0.58,
            eye_radius: 0.045,
        },
        AvatarKind::Astronaut => AvatarLayerAnchors {
            left_eye: (0.40, 0.47),
            right_eye: (0.60, 0.47),
            mouth: (0.50, 0.62),
            top: 0.19,
            neck: 0.76,
            face_width: 0.60,
            eye_radius: 0.045,
        },
        AvatarKind::Paws
        | AvatarKind::Planet
        | AvatarKind::Rocket
        | AvatarKind::Mushroom
        | AvatarKind::Cactus
        | AvatarKind::Cupcake
        | AvatarKind::Pizza
        | AvatarKind::Icecream
        | AvatarKind::Diamond
        | AvatarKind::CoffeeCup
        | AvatarKind::Shield => return None,
    };
    Some(anchors)
}

pub(crate) fn glasses_y_offset(kind: AvatarKind) -> f32 {
    match kind {
        AvatarKind::Dog | AvatarKind::Robot | AvatarKind::Ghost => 0.025,
        AvatarKind::Monster => 0.020,
        AvatarKind::Wizard | AvatarKind::Knight => 0.035,
        _ => 0.0,
    }
}

pub(crate) fn hat_y_offset(kind: AvatarKind) -> f32 {
    match kind {
        AvatarKind::Cat | AvatarKind::Frog => -0.035,
        _ => 0.0,
    }
}

pub(crate) fn crown_y_offset(kind: AvatarKind) -> f32 {
    match kind {
        AvatarKind::Cat => -0.035,
        AvatarKind::Alien | AvatarKind::Frog => -0.080,
        _ => 0.0,
    }
}

pub(crate) fn bowtie_y_offset(kind: AvatarKind) -> f32 {
    match kind {
        AvatarKind::Cat
        | AvatarKind::Fox
        | AvatarKind::Slime
        | AvatarKind::Wizard
        | AvatarKind::Octopus => 0.060,
        _ => 0.0,
    }
}

pub(crate) fn eyepatch_y_offset(kind: AvatarKind) -> f32 {
    match kind {
        AvatarKind::Knight => 0.030,
        _ => 0.0,
    }
}

pub(crate) fn horns_y_offset(kind: AvatarKind) -> f32 {
    match kind {
        AvatarKind::Dog | AvatarKind::Robot => 0.070,
        _ => 0.0,
    }
}

pub(crate) fn style_accent_color(color: AvatarColor, identity: &AvatarIdentity) -> Color {
    match color {
        AvatarColor::Default => hsl_to_color(180.0 + identity.unit_f32(25) * 160.0, 0.58, 0.48),
        AvatarColor::NeonMint => Color::rgb(27, 235, 179),
        AvatarColor::PastelPink => Color::rgb(246, 160, 196),
        AvatarColor::Crimson => Color::rgb(190, 18, 60),
        AvatarColor::Gold => Color::rgb(234, 179, 8),
        AvatarColor::DeepSeaBlue => Color::rgb(14, 116, 144),
    }
}

pub(crate) fn rgba_with_alpha(color: Color, alpha: u8) -> Rgba<u8> {
    Rgba([color.0[0], color.0[1], color.0[2], alpha])
}

pub(crate) fn blend_pixel(image: &mut RgbaImage, x: i32, y: i32, source: Rgba<u8>) {
    if !in_bounds(image, x, y) {
        return;
    }

    let destination = *image.get_pixel(x as u32, y as u32);
    let alpha = u32::from(source.0[3]);
    let inverse = 255 - alpha;
    let blended = Rgba([
        ((u32::from(source.0[0]) * alpha + u32::from(destination.0[0]) * inverse + 127) / 255)
            as u8,
        ((u32::from(source.0[1]) * alpha + u32::from(destination.0[1]) * inverse + 127) / 255)
            as u8,
        ((u32::from(source.0[2]) * alpha + u32::from(destination.0[2]) * inverse + 127) / 255)
            as u8,
        source.0[3].saturating_add(((u32::from(destination.0[3]) * inverse + 127) / 255) as u8),
    ]);
    image.put_pixel(x as u32, y as u32, blended);
}

pub(crate) fn draw_blended_rect_mut(image: &mut RgbaImage, rect: Rect, color: Rgba<u8>) {
    if image.width() == 0 || image.height() == 0 {
        return;
    }

    let bounds = Rect::at(0, 0).of_size(image.width(), image.height());
    if let Some(intersection) = bounds.intersect(rect) {
        for dy in 0..intersection.height() {
            for dx in 0..intersection.width() {
                let x = intersection.left() + dx as i32;
                let y = intersection.top() + dy as i32;
                blend_pixel(image, x, y, color);
            }
        }
    }
}

pub(crate) fn draw_style_color_layer(image: &mut RgbaImage, spec: AvatarSpec, accent: Color) {
    let width = spec.width as i32;
    let height = spec.height as i32;
    let bar_height = ((height as f32 * 0.08) as u32).max(3);
    draw_blended_rect_mut(
        image,
        Rect::at(0, height - bar_height as i32).of_size(spec.width, bar_height),
        rgba_with_alpha(accent, 210),
    );

    let stripe = ((spec.width.min(spec.height) as f32 * 0.03) as u32).max(2);
    draw_blended_rect_mut(
        image,
        Rect::at(0, 0).of_size(stripe, spec.height),
        rgba_with_alpha(accent, 145),
    );
    draw_blended_rect_mut(
        image,
        Rect::at(width - stripe as i32, 0).of_size(stripe, spec.height),
        rgba_with_alpha(accent, 145),
    );
}

pub(crate) fn draw_accessory_layer(
    image: &mut RgbaImage,
    spec: AvatarSpec,
    kind: AvatarKind,
    accessory: AvatarAccessory,
    accent: Color,
) {
    if accessory == AvatarAccessory::None {
        return;
    }

    let Some(anchors) = avatar_layer_anchors(kind) else {
        return;
    };

    let min = spec.width.min(spec.height) as i32;
    let (left_eye_x, left_eye_y) = anchors.point(spec, anchors.left_eye);
    let (right_eye_x, right_eye_y) = anchors.point(spec, anchors.right_eye);
    let (mouth_x, mouth_y) = anchors.point(spec, anchors.mouth);
    let eye_y = (left_eye_y + right_eye_y) / 2;
    let top_y = anchors.y(spec, anchors.top);
    let neck_y = anchors.y(spec, anchors.neck);
    let face_half = anchors.span(spec, anchors.face_width) / 2;
    let eye_radius = anchors.span(spec, anchors.eye_radius).max(3);
    let dark = Rgba([31, 41, 55, 255]);
    let light = rgba_with_alpha(accent, 255);

    match accessory {
        AvatarAccessory::None => {}
        AvatarAccessory::Glasses => {
            let radius = (eye_radius * 2).max(5);
            let glasses_offset = (glasses_y_offset(kind) * spec.height as f32).round() as i32;
            let left_glasses_y = left_eye_y + glasses_offset;
            let right_glasses_y = right_eye_y + glasses_offset;
            let bridge_y = (left_glasses_y + right_glasses_y) / 2;
            draw_hollow_circle_mut(image, (left_eye_x, left_glasses_y), radius, dark);
            draw_hollow_circle_mut(image, (right_eye_x, right_glasses_y), radius, dark);
            draw_line_segment_mut(
                image,
                ((left_eye_x + radius) as f32, bridge_y as f32),
                ((right_eye_x - radius) as f32, bridge_y as f32),
                dark,
            );
        }
        AvatarAccessory::Hat => {
            let brim_w = (face_half * 14 / 10).max(min * 20 / 100);
            let brim_h = (min * 5 / 100).max(2);
            let top_w = (brim_w * 7 / 10).max(min * 16 / 100);
            let top_h = min * 18 / 100;
            let y_offset = (hat_y_offset(kind) * spec.height as f32).round() as i32;
            let y = (top_y - top_h / 2 + y_offset).max(0);
            draw_filled_rect_mut(
                image,
                Rect::at(mouth_x - top_w / 2, y).of_size(top_w as u32, top_h as u32),
                light,
            );
            draw_filled_rect_mut(
                image,
                Rect::at(mouth_x - brim_w / 2, y + top_h).of_size(brim_w as u32, brim_h as u32),
                dark,
            );
        }
        AvatarAccessory::Headphones => {
            let side_r = (eye_radius * 2).max(5);
            let left_x = mouth_x - face_half;
            let right_x = mouth_x + face_half;
            draw_top_hollow_ellipse_mut(
                image,
                (mouth_x, eye_y),
                face_half,
                (eye_y - top_y).max(min * 8 / 100),
                dark,
            );
            draw_filled_circle_mut(image, (left_x, eye_y), side_r, light);
            draw_filled_circle_mut(image, (right_x, eye_y), side_r, light);
            draw_hollow_circle_mut(image, (left_x, eye_y), side_r, dark);
            draw_hollow_circle_mut(image, (right_x, eye_y), side_r, dark);
        }
        AvatarAccessory::Crown => {
            let y_offset = (crown_y_offset(kind) * spec.height as f32).round() as i32;
            let base_y = (top_y + y_offset).max(0);
            let points = [
                Point::new(mouth_x - face_half * 7 / 10, base_y + min * 12 / 100),
                Point::new(mouth_x - face_half * 45 / 100, base_y),
                Point::new(mouth_x - face_half * 16 / 100, base_y + min * 10 / 100),
                Point::new(mouth_x, (base_y - min * 4 / 100).max(0)),
                Point::new(mouth_x + face_half * 16 / 100, base_y + min * 10 / 100),
                Point::new(mouth_x + face_half * 45 / 100, base_y),
                Point::new(mouth_x + face_half * 7 / 10, base_y + min * 12 / 100),
            ];
            draw_polygon_mut(image, &points, light);
            draw_line_segment_mut(
                image,
                (
                    (mouth_x - face_half * 7 / 10) as f32,
                    (base_y + min * 12 / 100) as f32,
                ),
                (
                    (mouth_x + face_half * 7 / 10) as f32,
                    (base_y + min * 12 / 100) as f32,
                ),
                dark,
            );
        }
        AvatarAccessory::Bowtie => {
            let y_offset = (bowtie_y_offset(kind) * spec.height as f32).round() as i32;
            let y = neck_y + y_offset;
            let size = min * 10 / 100;
            let left = [
                Point::new(mouth_x, y),
                Point::new(mouth_x - size * 2, y - size),
                Point::new(mouth_x - size * 2, y + size),
            ];
            let right = [
                Point::new(mouth_x, y),
                Point::new(mouth_x + size * 2, y - size),
                Point::new(mouth_x + size * 2, y + size),
            ];
            draw_polygon_mut(image, &left, light);
            draw_polygon_mut(image, &right, light);
            draw_filled_circle_mut(image, (mouth_x, y), (size / 2).max(2), dark);
        }
        AvatarAccessory::Eyepatch => {
            let patch_rx = (eye_radius * 2).max(5);
            let patch_ry = (eye_radius * 3 / 2).max(4);
            let y_offset = (eyepatch_y_offset(kind) * spec.height as f32).round() as i32;
            let left_patch_y = left_eye_y + y_offset;
            let strap_start_y = top_y + y_offset / 2;
            let strap_end_y = mouth_y - eye_radius + y_offset;
            draw_line_segment_mut(
                image,
                ((mouth_x - face_half) as f32, strap_start_y as f32),
                ((mouth_x + face_half * 7 / 10) as f32, strap_end_y as f32),
                dark,
            );
            draw_filled_ellipse_mut(image, (left_eye_x, left_patch_y), patch_rx, patch_ry, dark);
        }
        AvatarAccessory::Scarf => {
            let scarf_h = (min * 8 / 100).max(4);
            let y = neck_y;
            draw_filled_rect_mut(
                image,
                Rect::at(mouth_x - face_half * 8 / 10, y)
                    .of_size((face_half * 16 / 10) as u32, scarf_h as u32),
                light,
            );
            draw_filled_rect_mut(
                image,
                Rect::at(mouth_x + face_half / 5, y + scarf_h / 2)
                    .of_size((min * 9 / 100) as u32, (min * 20 / 100) as u32),
                light,
            );
        }
        AvatarAccessory::Halo => {
            draw_hollow_ellipse_mut(
                image,
                (mouth_x, (top_y - min * 7 / 100).max(0)),
                (face_half * 7 / 10).max(min * 10 / 100),
                min * 7 / 100,
                light,
            );
        }
        AvatarAccessory::Horns => {
            let y_offset = (horns_y_offset(kind) * spec.height as f32).round() as i32;
            let y = top_y + min * 5 / 100 + y_offset;
            let left = [
                Point::new(mouth_x - face_half * 6 / 10, y + min * 7 / 100),
                Point::new(mouth_x - face_half, (y - min * 12 / 100).max(0)),
                Point::new(mouth_x - face_half * 3 / 10, y + min * 2 / 100),
            ];
            let right = [
                Point::new(mouth_x + face_half * 6 / 10, y + min * 7 / 100),
                Point::new(mouth_x + face_half, (y - min * 12 / 100).max(0)),
                Point::new(mouth_x + face_half * 3 / 10, y + min * 2 / 100),
            ];
            draw_polygon_mut(image, &left, light);
            draw_polygon_mut(image, &right, light);
        }
    }
}

pub(crate) fn draw_expression_layer(
    image: &mut RgbaImage,
    spec: AvatarSpec,
    kind: AvatarKind,
    expression: AvatarExpression,
    accent: Color,
) {
    if expression == AvatarExpression::Default {
        return;
    }

    let Some(anchors) = avatar_layer_anchors(kind) else {
        return;
    };

    let min = spec.width.min(spec.height) as i32;
    let (left_eye_x, left_eye_y) = anchors.point(spec, anchors.left_eye);
    let (right_eye_x, right_eye_y) = anchors.point(spec, anchors.right_eye);
    let (mouth_x, mouth_y) = anchors.point(spec, anchors.mouth);
    let eye_radius = anchors.span(spec, anchors.eye_radius).max(3);
    let dark = Rgba([17, 24, 39, 255]);
    let accent = rgba_with_alpha(accent, 255);

    match expression {
        AvatarExpression::Default => {}
        AvatarExpression::Happy => {
            draw_smile_curve(
                image,
                mouth_x,
                mouth_y,
                min * 14 / 100,
                min * 8 / 100,
                false,
                dark,
            );
        }
        AvatarExpression::Grumpy => {
            draw_smile_curve(
                image,
                mouth_x,
                mouth_y + min * 7 / 100,
                min * 14 / 100,
                min * 8 / 100,
                true,
                dark,
            );
        }
        AvatarExpression::Surprised => {
            draw_hollow_circle_mut(image, (mouth_x, mouth_y), (min * 7 / 100).max(3), dark);
        }
        AvatarExpression::Sleepy => {
            draw_line_segment_mut(
                image,
                ((left_eye_x - eye_radius) as f32, left_eye_y as f32),
                ((left_eye_x + eye_radius) as f32, left_eye_y as f32),
                dark,
            );
            draw_line_segment_mut(
                image,
                ((right_eye_x - eye_radius) as f32, right_eye_y as f32),
                ((right_eye_x + eye_radius) as f32, right_eye_y as f32),
                dark,
            );
        }
        AvatarExpression::Winking => {
            draw_line_segment_mut(
                image,
                ((left_eye_x - eye_radius) as f32, left_eye_y as f32),
                ((left_eye_x + eye_radius) as f32, left_eye_y as f32),
                dark,
            );
            draw_filled_circle_mut(
                image,
                (right_eye_x, right_eye_y),
                (eye_radius * 7 / 10).max(2),
                dark,
            );
        }
        AvatarExpression::Cool => {
            let rect_h = (eye_radius * 2).max(4) as u32;
            let left = left_eye_x.min(right_eye_x) - eye_radius * 2;
            let width = (right_eye_x.max(left_eye_x) - left + eye_radius * 2).max(min * 24 / 100);
            draw_filled_rect_mut(
                image,
                Rect::at(left, left_eye_y.min(right_eye_y) - eye_radius)
                    .of_size(width as u32, rect_h),
                dark,
            );
            draw_blended_rect_mut(
                image,
                Rect::at(
                    left + eye_radius,
                    left_eye_y.min(right_eye_y) - eye_radius / 2,
                )
                .of_size((width / 3).max(2) as u32, (eye_radius / 2).max(2) as u32),
                Rgba([255, 255, 255, 70]),
            );
        }
        AvatarExpression::Crying => {
            draw_smile_curve(
                image,
                mouth_x,
                mouth_y + min * 7 / 100,
                min * 12 / 100,
                min * 7 / 100,
                true,
                dark,
            );
            draw_filled_ellipse_mut(
                image,
                (right_eye_x + eye_radius, right_eye_y + eye_radius * 2),
                (eye_radius * 7 / 10).max(2),
                (eye_radius * 3 / 2).max(4),
                accent,
            );
        }
    }
}

pub(crate) fn draw_smile_curve(
    image: &mut RgbaImage,
    cx: i32,
    cy: i32,
    width: i32,
    height: i32,
    inverted: bool,
    color: Rgba<u8>,
) {
    let mut previous = None;
    for step in 0..=16 {
        let t = step as f32 / 16.0;
        let x = cx - width + (2.0 * width as f32 * t) as i32;
        let curve = (1.0 - (2.0 * t - 1.0).powi(2)) * height as f32;
        let y = if inverted {
            cy - curve as i32
        } else {
            cy + curve as i32
        };
        if let Some((px, py)) = previous {
            draw_antialiased_line_segment_mut(image, (px, py), (x, y), color, interpolate);
        }
        previous = Some((x, y));
    }
}

pub(crate) fn draw_hollow_ellipse_mut(
    image: &mut RgbaImage,
    center: (i32, i32),
    width_radius: i32,
    height_radius: i32,
    color: Rgba<u8>,
) {
    let (cx, cy) = center;
    let steps = (width_radius.max(height_radius) * 8).max(24);
    let mut previous = None;
    for step in 0..=steps {
        let angle = step as f32 / steps as f32 * std::f32::consts::TAU;
        let x = cx + (angle.cos() * width_radius as f32).round() as i32;
        let y = cy + (angle.sin() * height_radius as f32).round() as i32;
        if let Some((px, py)) = previous {
            draw_antialiased_line_segment_mut(image, (px, py), (x, y), color, interpolate);
        }
        previous = Some((x, y));
    }
}

pub(crate) fn draw_top_hollow_ellipse_mut(
    image: &mut RgbaImage,
    center: (i32, i32),
    width_radius: i32,
    height_radius: i32,
    color: Rgba<u8>,
) {
    let (cx, cy) = center;
    let steps = (width_radius.max(height_radius) * 4).max(16);
    let mut previous = None;
    for step in 0..=steps {
        let angle = std::f32::consts::PI + step as f32 / steps as f32 * std::f32::consts::PI;
        let x = cx + (angle.cos() * width_radius as f32).round() as i32;
        let y = cy + (angle.sin() * height_radius as f32).round() as i32;
        if let Some((px, py)) = previous {
            draw_antialiased_line_segment_mut(image, (px, py), (x, y), color, interpolate);
        }
        previous = Some((x, y));
    }
}

pub(crate) fn apply_shape_layer(
    image: &mut RgbaImage,
    spec: AvatarSpec,
    shape: AvatarShape,
    accent: Color,
) {
    if shape == AvatarShape::Square {
        return;
    }

    for y in 0..spec.height {
        for x in 0..spec.width {
            if !point_inside_avatar_shape(x as i32, y as i32, spec, shape) {
                image.put_pixel(x, y, Rgba([255, 255, 255, 0]));
            }
        }
    }

    let frame = rgba_with_alpha(accent, 255);
    for y in 0..spec.height as i32 {
        for x in 0..spec.width as i32 {
            if point_inside_avatar_shape(x, y, spec, shape)
                && (!point_inside_avatar_shape(x - 1, y, spec, shape)
                    || !point_inside_avatar_shape(x + 1, y, spec, shape)
                    || !point_inside_avatar_shape(x, y - 1, spec, shape)
                    || !point_inside_avatar_shape(x, y + 1, spec, shape))
            {
                image.put_pixel(x as u32, y as u32, frame);
            }
        }
    }
}

pub(crate) fn point_inside_avatar_shape(
    x: i32,
    y: i32,
    spec: AvatarSpec,
    shape: AvatarShape,
) -> bool {
    if x < 0 || y < 0 || x >= spec.width as i32 || y >= spec.height as i32 {
        return false;
    }

    let w = i64::from(spec.width);
    let h = i64::from(spec.height);
    let x2 = i64::from(x) * 2 + 1;
    let y2 = i64::from(y) * 2 + 1;

    match shape {
        AvatarShape::Square => true,
        AvatarShape::Circle => {
            let dx = x2 - w;
            let dy = y2 - h;
            dx * dx * h * h + dy * dy * w * w <= w * w * h * h
        }
        AvatarShape::Squircle => {
            let r = (w.min(h) * 18 / 100).max(1);
            let inner_left = r;
            let inner_right = w - r;
            let inner_top = r;
            let inner_bottom = h - r;
            if (x2 >= inner_left * 2 && x2 <= inner_right * 2)
                || (y2 >= inner_top * 2 && y2 <= inner_bottom * 2)
            {
                true
            } else {
                let cx = if x2 < inner_left * 2 {
                    inner_left
                } else {
                    inner_right
                };
                let cy = if y2 < inner_top * 2 {
                    inner_top
                } else {
                    inner_bottom
                };
                let dx = x2 - cx * 2;
                let dy = y2 - cy * 2;
                dx * dx + dy * dy <= (r * 2) * (r * 2)
            }
        }
        AvatarShape::Hexagon => point_inside_percent_polygon(
            x,
            y,
            spec,
            &[(25, 0), (75, 0), (100, 50), (75, 100), (25, 100), (0, 50)],
        ),
        AvatarShape::Octagon => point_inside_percent_polygon(
            x,
            y,
            spec,
            &[
                (30, 0),
                (70, 0),
                (100, 30),
                (100, 70),
                (70, 100),
                (30, 100),
                (0, 70),
                (0, 30),
            ],
        ),
    }
}

pub(crate) fn point_inside_percent_polygon(
    x: i32,
    y: i32,
    spec: AvatarSpec,
    points: &[(i64, i64)],
) -> bool {
    let mut inside = false;
    let width = i64::from(spec.width);
    let height = i64::from(spec.height);
    let px = (i64::from(x) * 2 + 1) * 50;
    let py = (i64::from(y) * 2 + 1) * 50;
    let Some(&last) = points.last() else {
        return false;
    };
    let mut previous = last;
    for &current in points {
        let xi = current.0 * width;
        let yi = current.1 * height;
        let xj = previous.0 * width;
        let yj = previous.1 * height;
        let crosses = (yi > py) != (yj > py);
        if crosses {
            let lhs = (px - xi) * (yj - yi);
            let rhs = (xj - xi) * (py - yi);
            let is_left = if yj > yi { lhs < rhs } else { lhs > rhs };
            if is_left {
                inside = !inside;
            }
        }
        previous = current;
    }
    inside
}

pub(crate) fn render_style_svg_layers(
    spec: AvatarSpec,
    style: AvatarStyleOptions,
    identity: &AvatarIdentity,
) -> String {
    if !style.has_extra_layers() {
        return String::new();
    }

    let accent = style_accent_color(style.color, identity);
    let mut svg = String::new();
    if style.color != AvatarColor::Default {
        svg.push_str(&render_color_svg_layer(spec, accent));
    }
    svg.push_str(&render_expression_svg_layer(
        spec,
        style.kind,
        style.expression,
        accent,
    ));
    svg.push_str(&render_accessory_svg_layer(
        spec,
        style.kind,
        style.accessory,
        accent,
    ));
    svg
}

pub(crate) fn render_color_svg_layer(spec: AvatarSpec, accent: Color) -> String {
    let bar_height = spec.height as f32 * 0.08;
    let stripe = spec.width.min(spec.height) as f32 * 0.03;
    format!(
        r#"<g data-layer="color"><rect x="0" y="{bar_y}" width="{w}" height="{bar_h}" fill="{fill}" opacity="0.82"/><rect x="0" y="0" width="{stripe}" height="{h}" fill="{fill}" opacity="0.56"/><rect x="{right}" y="0" width="{stripe}" height="{h}" fill="{fill}" opacity="0.56"/></g>"#,
        bar_y = spec.height as f32 - bar_height,
        w = spec.width,
        h = spec.height,
        bar_h = bar_height,
        stripe = stripe,
        right = spec.width as f32 - stripe,
        fill = color_hex(accent),
    )
}

pub(crate) fn render_accessory_svg_layer(
    spec: AvatarSpec,
    kind: AvatarKind,
    accessory: AvatarAccessory,
    accent: Color,
) -> String {
    if accessory == AvatarAccessory::None {
        return String::new();
    }

    let Some(anchors) = avatar_layer_anchors(kind) else {
        return String::new();
    };

    let min = spec.width.min(spec.height) as f32;
    let (left_eye_x, left_eye_y) = (
        anchors.left_eye.0 * spec.width as f32,
        anchors.left_eye.1 * spec.height as f32,
    );
    let (right_eye_x, right_eye_y) = (
        anchors.right_eye.0 * spec.width as f32,
        anchors.right_eye.1 * spec.height as f32,
    );
    let (mouth_x, mouth_y) = (
        anchors.mouth.0 * spec.width as f32,
        anchors.mouth.1 * spec.height as f32,
    );
    let eye_y = (left_eye_y + right_eye_y) / 2.0;
    let top_y = anchors.top * spec.height as f32;
    let neck_y = anchors.neck * spec.height as f32;
    let face_half = anchors.face_width * min / 2.0;
    let eye_radius = (anchors.eye_radius * min).max(3.0);
    let glasses_offset = glasses_y_offset(kind) * spec.height as f32;
    let fill = color_hex(accent);
    let dark = "#1f2937";
    let layer = accessory.as_str();

    let body = match accessory {
        AvatarAccessory::None => String::new(),
        AvatarAccessory::Glasses => format!(
            r#"<circle cx="{lx}" cy="{y}" r="{r}" fill="none" stroke="{dark}" stroke-width="3"/><circle cx="{rx}" cy="{y}" r="{r}" fill="none" stroke="{dark}" stroke-width="3"/><line x1="{l2}" y1="{y}" x2="{r2}" y2="{y}" stroke="{dark}" stroke-width="3"/>"#,
            lx = left_eye_x,
            rx = right_eye_x,
            y = eye_y + glasses_offset,
            r = eye_radius * 2.0,
            l2 = left_eye_x + eye_radius * 2.0,
            r2 = right_eye_x - eye_radius * 2.0,
        ),
        AvatarAccessory::Hat => format!(
            r#"<rect x="{x}" y="{y}" width="{tw}" height="{th}" fill="{fill}"/><rect x="{bx}" y="{by}" width="{bw}" height="{bh}" fill="{dark}"/>"#,
            x = mouth_x - face_half * 0.35,
            y = (top_y - min * 0.09 + hat_y_offset(kind) * spec.height as f32).max(0.0),
            tw = face_half * 0.70,
            th = min * 0.18,
            bx = mouth_x - face_half * 0.70,
            by = (top_y - min * 0.09 + hat_y_offset(kind) * spec.height as f32).max(0.0)
                + min * 0.18,
            bw = face_half * 1.40,
            bh = min * 0.05,
        ),
        AvatarAccessory::Headphones => format!(
            r#"<path d="M {l} {y} Q {cx} {top} {rr} {y}" fill="none" stroke="{dark}" stroke-width="4" stroke-linecap="round"/><circle cx="{l}" cy="{ear_y}" r="{er}" fill="{fill}" stroke="{dark}" stroke-width="2"/><circle cx="{rr}" cy="{ear_y}" r="{er}" fill="{fill}" stroke="{dark}" stroke-width="2"/>"#,
            l = mouth_x - face_half,
            rr = mouth_x + face_half,
            cx = mouth_x,
            y = eye_y,
            top = top_y,
            ear_y = eye_y,
            er = eye_radius * 2.0,
        ),
        AvatarAccessory::Crown => {
            let crown_top_y = (top_y + crown_y_offset(kind) * spec.height as f32).max(0.0);
            let p = format!(
                "{},{} {},{} {},{} {},{} {},{} {},{} {},{}",
                mouth_x - face_half * 0.70,
                crown_top_y + min * 0.12,
                mouth_x - face_half * 0.45,
                crown_top_y,
                mouth_x - face_half * 0.16,
                crown_top_y + min * 0.10,
                mouth_x,
                (crown_top_y - min * 0.04).max(0.0),
                mouth_x + face_half * 0.16,
                crown_top_y + min * 0.10,
                mouth_x + face_half * 0.45,
                crown_top_y,
                mouth_x + face_half * 0.70,
                crown_top_y + min * 0.12
            );
            format!(
                r#"<polygon points="{p}" fill="{fill}"/><line x1="{x1}" y1="{y}" x2="{x2}" y2="{y}" stroke="{dark}" stroke-width="3"/>"#,
                x1 = mouth_x - face_half * 0.70,
                x2 = mouth_x + face_half * 0.70,
                y = crown_top_y + min * 0.12,
            )
        }
        AvatarAccessory::Bowtie => {
            let bowtie_y = neck_y + bowtie_y_offset(kind) * spec.height as f32;
            let lp = format!(
                "{},{} {},{} {},{}",
                mouth_x,
                bowtie_y,
                mouth_x - min * 0.20,
                bowtie_y - min * 0.10,
                mouth_x - min * 0.20,
                bowtie_y + min * 0.10
            );
            let rp = format!(
                "{},{} {},{} {},{}",
                mouth_x,
                bowtie_y,
                mouth_x + min * 0.20,
                bowtie_y - min * 0.10,
                mouth_x + min * 0.20,
                bowtie_y + min * 0.10
            );
            format!(
                r#"<polygon points="{lp}" fill="{fill}"/><polygon points="{rp}" fill="{fill}"/><circle cx="{cx}" cy="{y}" r="{r}" fill="{dark}"/>"#,
                cx = mouth_x,
                y = bowtie_y,
                r = min * 0.05,
            )
        }
        AvatarAccessory::Eyepatch => format!(
            r#"<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{dark}" stroke-width="3"/><ellipse cx="{cx}" cy="{cy}" rx="{rx}" ry="{ry}" fill="{dark}"/>"#,
            x1 = mouth_x - face_half,
            y1 = top_y + eyepatch_y_offset(kind) * spec.height as f32 * 0.50,
            x2 = mouth_x + face_half * 0.70,
            y2 = mouth_y - eye_radius + eyepatch_y_offset(kind) * spec.height as f32,
            cx = left_eye_x,
            cy = left_eye_y + eyepatch_y_offset(kind) * spec.height as f32,
            rx = eye_radius * 2.0,
            ry = eye_radius * 1.5,
        ),
        AvatarAccessory::Scarf => format!(
            r#"<rect x="{x}" y="{y}" width="{sw}" height="{sh}" fill="{fill}"/><rect x="{tx}" y="{ty}" width="{tw}" height="{th}" fill="{fill}"/>"#,
            x = mouth_x - face_half * 0.80,
            y = neck_y,
            sw = face_half * 1.60,
            sh = min * 0.08,
            tx = mouth_x + face_half * 0.20,
            ty = neck_y + min * 0.04,
            tw = min * 0.09,
            th = min * 0.20,
        ),
        AvatarAccessory::Halo => format!(
            r#"<ellipse cx="{cx}" cy="{cy}" rx="{rx}" ry="{ry}" fill="none" stroke="{fill}" stroke-width="3"/>"#,
            cx = mouth_x,
            cy = (top_y - min * 0.07).max(0.0),
            rx = (face_half * 0.70).max(min * 0.10),
            ry = min * 0.07,
        ),
        AvatarAccessory::Horns => {
            let horn_top_y = top_y + horns_y_offset(kind) * spec.height as f32;
            let lp = format!(
                "{},{} {},{} {},{}",
                mouth_x - face_half * 0.60,
                horn_top_y + min * 0.12,
                mouth_x - face_half,
                (horn_top_y - min * 0.12).max(0.0),
                mouth_x - face_half * 0.30,
                horn_top_y + min * 0.07
            );
            let rp = format!(
                "{},{} {},{} {},{}",
                mouth_x + face_half * 0.60,
                horn_top_y + min * 0.12,
                mouth_x + face_half,
                (horn_top_y - min * 0.12).max(0.0),
                mouth_x + face_half * 0.30,
                horn_top_y + min * 0.07
            );
            format!(
                r#"<polygon points="{lp}" fill="{fill}"/><polygon points="{rp}" fill="{fill}"/>"#
            )
        }
    };

    format!(r#"<g data-layer="accessory-{layer}">{body}</g>"#)
}

pub(crate) fn render_expression_svg_layer(
    spec: AvatarSpec,
    kind: AvatarKind,
    expression: AvatarExpression,
    accent: Color,
) -> String {
    if expression == AvatarExpression::Default {
        return String::new();
    }

    let Some(anchors) = avatar_layer_anchors(kind) else {
        return String::new();
    };

    let min = spec.width.min(spec.height) as f32;
    let left_eye_x = anchors.left_eye.0 * spec.width as f32;
    let left_eye_y = anchors.left_eye.1 * spec.height as f32;
    let right_eye_x = anchors.right_eye.0 * spec.width as f32;
    let right_eye_y = anchors.right_eye.1 * spec.height as f32;
    let mouth_x = anchors.mouth.0 * spec.width as f32;
    let mouth_y = anchors.mouth.1 * spec.height as f32;
    let eye_radius = (anchors.eye_radius * min).max(3.0);
    let dark = "#111827";
    let layer = expression.as_str();
    let body = match expression {
        AvatarExpression::Default => String::new(),
        AvatarExpression::Happy => format!(
            r#"<path d="M {x1} {y} Q {cx} {my} {x2} {y}" fill="none" stroke="{dark}" stroke-width="3" stroke-linecap="round"/>"#,
            x1 = mouth_x - min * 0.14,
            x2 = mouth_x + min * 0.14,
            y = mouth_y,
            cx = mouth_x,
            my = mouth_y + min * 0.09,
        ),
        AvatarExpression::Grumpy => format!(
            r#"<path d="M {x1} {y} Q {cx} {my} {x2} {y}" fill="none" stroke="{dark}" stroke-width="3" stroke-linecap="round"/>"#,
            x1 = mouth_x - min * 0.14,
            x2 = mouth_x + min * 0.14,
            y = mouth_y,
            cx = mouth_x,
            my = mouth_y - min * 0.09,
        ),
        AvatarExpression::Surprised => format!(
            r#"<circle cx="{cx}" cy="{y}" r="{r}" fill="none" stroke="{dark}" stroke-width="3"/>"#,
            cx = mouth_x,
            y = mouth_y,
            r = min * 0.07,
        ),
        AvatarExpression::Sleepy => format!(
            r#"<line x1="{x1}" y1="{ey}" x2="{x2}" y2="{ey}" stroke="{dark}" stroke-width="3" stroke-linecap="round"/><line x1="{x3}" y1="{ey}" x2="{x4}" y2="{ey}" stroke="{dark}" stroke-width="3" stroke-linecap="round"/>"#,
            x1 = left_eye_x - eye_radius,
            x2 = left_eye_x + eye_radius,
            x3 = right_eye_x - eye_radius,
            x4 = right_eye_x + eye_radius,
            ey = (left_eye_y + right_eye_y) / 2.0,
        ),
        AvatarExpression::Winking => format!(
            r#"<line x1="{x1}" y1="{ey}" x2="{x2}" y2="{ey}" stroke="{dark}" stroke-width="3" stroke-linecap="round"/><circle cx="{rx}" cy="{ey}" r="{r}" fill="{dark}"/>"#,
            x1 = left_eye_x - eye_radius,
            x2 = left_eye_x + eye_radius,
            rx = right_eye_x,
            ey = right_eye_y,
            r = eye_radius * 0.70,
        ),
        AvatarExpression::Cool => format!(
            r#"<rect x="{x}" y="{ey}" width="{ww}" height="{hh}" fill="{dark}"/>"#,
            x = left_eye_x.min(right_eye_x) - eye_radius * 2.0,
            ey = left_eye_y.min(right_eye_y) - eye_radius,
            ww = right_eye_x.max(left_eye_x) - left_eye_x.min(right_eye_x) + eye_radius * 4.0,
            hh = eye_radius * 2.0,
        ),
        AvatarExpression::Crying => format!(
            r#"<path d="M {x1} {y} Q {cx} {my} {x2} {y}" fill="none" stroke="{dark}" stroke-width="3" stroke-linecap="round"/><ellipse cx="{tx}" cy="{ty}" rx="{rx}" ry="{ry}" fill="{fill}"/>"#,
            x1 = mouth_x - min * 0.12,
            x2 = mouth_x + min * 0.12,
            y = mouth_y,
            cx = mouth_x,
            my = mouth_y - min * 0.08,
            tx = right_eye_x + eye_radius,
            ty = right_eye_y + eye_radius * 2.0,
            rx = eye_radius * 0.70,
            ry = eye_radius * 1.50,
            fill = color_hex(accent),
        ),
    };
    format!(r#"<g data-layer="expression-{layer}">{body}</g>"#)
}

pub(crate) fn render_shape_svg_layer(
    spec: AvatarSpec,
    shape: AvatarShape,
    accent: Color,
) -> String {
    if shape == AvatarShape::Square {
        return String::new();
    }

    let w = spec.width as f32;
    let h = spec.height as f32;
    let fill = color_hex(accent);
    let body = match shape {
        AvatarShape::Square => String::new(),
        AvatarShape::Circle => format!(
            r#"<ellipse cx="{cx}" cy="{cy}" rx="{rx}" ry="{ry}" fill="none" stroke="{fill}" stroke-width="3"/>"#,
            cx = w / 2.0,
            cy = h / 2.0,
            rx = w / 2.0 - 1.5,
            ry = h / 2.0 - 1.5,
        ),
        AvatarShape::Squircle => format!(
            r#"<rect x="1.5" y="1.5" width="{rw}" height="{rh}" rx="{r}" ry="{r}" fill="none" stroke="{fill}" stroke-width="3"/>"#,
            rw = w - 3.0,
            rh = h - 3.0,
            r = w.min(h) * 0.18,
        ),
        AvatarShape::Hexagon => {
            let points = format!(
                "{},{} {},{} {},{} {},{} {},{} {},{}",
                w * 0.25,
                1.5,
                w * 0.75,
                1.5,
                w - 1.5,
                h * 0.5,
                w * 0.75,
                h - 1.5,
                w * 0.25,
                h - 1.5,
                1.5,
                h * 0.5
            );
            format!(r#"<polygon points="{points}" fill="none" stroke="{fill}" stroke-width="3"/>"#)
        }
        AvatarShape::Octagon => {
            let points = format!(
                "{},{} {},{} {},{} {},{} {},{} {},{} {},{} {},{}",
                w * 0.30,
                1.5,
                w * 0.70,
                1.5,
                w - 1.5,
                h * 0.30,
                w - 1.5,
                h * 0.70,
                w * 0.70,
                h - 1.5,
                w * 0.30,
                h - 1.5,
                1.5,
                h * 0.70,
                1.5,
                h * 0.30
            );
            format!(r#"<polygon points="{points}" fill="none" stroke="{fill}" stroke-width="3"/>"#)
        }
    };
    format!(r#"<g data-layer="shape-{}">{body}</g>"#, shape.as_str())
}

pub(crate) fn render_shape_svg_clip(
    spec: AvatarSpec,
    shape: AvatarShape,
    content: &str,
) -> (String, String) {
    if shape == AvatarShape::Square {
        return (String::new(), content.to_owned());
    }

    let clip_id = "hashavatar-frame-clip";
    let clip = render_shape_svg_clip_body(spec, shape);
    (
        format!(r#"<defs><clipPath id="{clip_id}">{clip}</clipPath></defs>"#),
        format!(r#"<g clip-path="url(#{clip_id})">{content}</g>"#),
    )
}

pub(crate) fn render_shape_svg_clip_body(spec: AvatarSpec, shape: AvatarShape) -> String {
    let w = spec.width as f32;
    let h = spec.height as f32;
    match shape {
        AvatarShape::Square => format!(r#"<rect x="0" y="0" width="{w}" height="{h}"/>"#),
        AvatarShape::Circle => format!(
            r#"<ellipse cx="{cx}" cy="{cy}" rx="{rx}" ry="{ry}"/>"#,
            cx = w / 2.0,
            cy = h / 2.0,
            rx = w / 2.0,
            ry = h / 2.0,
        ),
        AvatarShape::Squircle => format!(
            r#"<rect x="0" y="0" width="{w}" height="{h}" rx="{r}" ry="{r}"/>"#,
            r = w.min(h) * 0.18,
        ),
        AvatarShape::Hexagon => {
            let points = format!(
                "{},{} {},{} {},{} {},{} {},{} {},{}",
                w * 0.25,
                0.0,
                w * 0.75,
                0.0,
                w,
                h * 0.5,
                w * 0.75,
                h,
                w * 0.25,
                h,
                0.0,
                h * 0.5
            );
            format!(r#"<polygon points="{points}"/>"#)
        }
        AvatarShape::Octagon => {
            let points = format!(
                "{},{} {},{} {},{} {},{} {},{} {},{} {},{} {},{}",
                w * 0.30,
                0.0,
                w * 0.70,
                0.0,
                w,
                h * 0.30,
                w,
                h * 0.70,
                w * 0.70,
                h,
                w * 0.30,
                h,
                0.0,
                h * 0.70,
                0.0,
                h * 0.30
            );
            format!(r#"<polygon points="{points}"/>"#)
        }
    }
}
