package database

import (
	"context"
	"crypto/md5"
	"database/sql"
	"encoding/hex"
	"fmt"
	"math"
	"time"
)

// UpsertRainTile stores a rain tile into table `rain_tiles`.
// The tiling scheme is equal-angle EPSG:4326 like radar tiles.
func UpsertRainTile(ctx context.Context, db *sql.DB, product string, dt time.Time, z, y, x int, width, height int, data []byte) error {
	if width == 0 {
		width = 256
	}
	if height == 0 {
		height = 256
	}
	step := 360.0 / math.Pow(2, float64(z))
	west := -180.0 + float64(x)*step
	south := -90.0 + float64(y)*step
	east := west + step
	north := south + step
	res := step / float64(width)

	sum := md5.Sum(data)
	md5hex := hex.EncodeToString(sum[:])

	q := `
    INSERT INTO rain_tiles (
        product, dt, z, y, x, width, height,
        west, south, east, north, res_deg,
        data, checksum_md5
    ) VALUES (
        $1,$2,$3,$4,$5,$6,$7,
        $8,$9,$10,$11,$12,
        $13,$14
    )
    ON CONFLICT (product, dt, z, y, x)
    DO UPDATE SET
        width = EXCLUDED.width,
        height = EXCLUDED.height,
        west = EXCLUDED.west,
        south = EXCLUDED.south,
        east = EXCLUDED.east,
        north = EXCLUDED.north,
        res_deg = EXCLUDED.res_deg,
        data = EXCLUDED.data,
        checksum_md5 = EXCLUDED.checksum_md5`

	_, err := db.ExecContext(ctx, q,
		product, dt, z, y, x, width, height,
		west, south, east, north, res,
		data, md5hex,
	)
	if err != nil {
		return fmt.Errorf("upsert rain tile (%s %s z=%d y=%d x=%d): %w", product, dt.Format(time.RFC3339), z, y, x, err)
	}
	return nil
}

// HasRainTile reports whether a rain tile exists for the given key.
func HasRainTile(ctx context.Context, db *sql.DB, product string, dt time.Time, z, y, x int) (bool, error) {
	const q = `SELECT 1 FROM rain_tiles WHERE product=$1 AND dt=$2 AND z=$3 AND y=$4 AND x=$5 LIMIT 1`
	var one int
	err := db.QueryRowContext(ctx, q, product, dt, z, y, x).Scan(&one)
	if err == sql.ErrNoRows {
		return false, nil
	}
	if err != nil {
		return false, fmt.Errorf("check rain tile exists: %w", err)
	}
	return true, nil
}
