package rankcard

import (
	"bytes"
	"context"
	"fmt"
	"image"
	"image/png"
	"io"
	"math"
	"net/http"
	"time"

	"github.com/fogleman/gg"
	"golang.org/x/image/draw"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/goregular"
	"golang.org/x/image/font/opentype"
)

type Data struct {
	Name    string
	Level   int
	XP      int64
	Percent float64 // 0..1 progress towards next level
	Avatar  string  // URL
}

func RenderPNG(ctx context.Context, d Data) ([]byte, error) {
	const (
		w  = 900
		h  = 300
		px = 30

		avatarSize = 150
		barX       = 30
		barY       = 220
		barW       = 650
		barH       = 40
	)

	percent := clamp01(d.Percent)

	dc := gg.NewContext(w, h)
	dc.SetHexColor("#141414")
	dc.Clear()

	// Right white polygon
	dc.SetHexColor("#FFFFFF")
	dc.NewSubPath()
	dc.MoveTo(600, 0)
	dc.LineTo(750, 300)
	dc.LineTo(900, 300)
	dc.LineTo(900, 0)
	dc.ClosePath()
	dc.Fill()

	// Avatar (circle clip)
	if img, err := fetchImage(ctx, d.Avatar); err == nil && img != nil {
		img = resizeImage(img, avatarSize, avatarSize)
		dc.Push()
		dc.DrawCircle(px+avatarSize/2, px+avatarSize/2, avatarSize/2)
		dc.Clip()
		dc.DrawImage(img, px, px)
		dc.Pop()
		// Defensive: ensure no clip leaks past avatar drawing.
		dc.ResetClip()
	}

	// Progress bar background
	dc.SetHexColor("#FFFFFF")
	dc.DrawRoundedRectangle(barX, barY, barW, barH, 20)
	dc.Fill()

	// Progress bar fill
	fillW := barW * percent
	if fillW > 0 {
		dc.SetHexColor("#282828")
		// Keep radius from looking weird on small widths
		r := math.Min(20, fillW/2)
		dc.DrawRoundedRectangle(barX, barY, fillW, barH, r)
		dc.Fill()
	}

	// Text
	titleFace, smallFace, err := loadFontFaces()
	if err != nil {
		return nil, err
	}

	dc.SetHexColor("#FFFFFF")
	dc.SetFontFace(titleFace)
	dc.DrawStringAnchored(d.Name, 200, 60, 0, 0.5)

	dc.SetLineWidth(2)
	dc.DrawLine(200, 100, 550, 100)
	dc.Stroke()

	dc.SetFontFace(smallFace)
	dc.DrawStringAnchored(
		fmt.Sprintf("Level - %d | XP - %d | %d%%", d.Level, d.XP, int64(math.Round(percent*100))),
		200,
		145,
		0,
		0.5,
	)

	// Watermark to verify latest render is running
	dc.SetFontFace(smallFace)
	dc.SetHexColor("#FFCC00")
	dc.DrawStringAnchored("velox-go rankcard", 890, 290, 1, 1)

	var buf bytes.Buffer
	if err := png.Encode(&buf, dc.Image()); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func clamp01(v float64) float64 {
	if v < 0 {
		return 0
	}
	if v > 1 {
		return 1
	}
	return v
}

func loadFontFaces() (title font.Face, small font.Face, err error) {
	f, err := opentype.Parse(goregular.TTF)
	if err != nil {
		return nil, nil, err
	}

	title, err = opentype.NewFace(f, &opentype.FaceOptions{Size: 40, DPI: 72, Hinting: font.HintingFull})
	if err != nil {
		return nil, nil, err
	}
	small, err = opentype.NewFace(f, &opentype.FaceOptions{Size: 30, DPI: 72, Hinting: font.HintingFull})
	if err != nil {
		return nil, nil, err
	}

	return title, small, nil
}

func fetchImage(ctx context.Context, url string) (image.Image, error) {
	if url == "" {
		return nil, fmt.Errorf("empty avatar url")
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		io.Copy(io.Discard, resp.Body)
		return nil, fmt.Errorf("avatar fetch status %d", resp.StatusCode)
	}

	img, _, err := image.Decode(resp.Body)
	return img, err
}

func resizeImage(src image.Image, w, h int) image.Image {
	dst := image.NewRGBA(image.Rect(0, 0, w, h))
	draw.CatmullRom.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Over, nil)
	return dst
}
