// backend/uploads.go
package main

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/tus/tusd/pkg/handler"

	"scm.bstein.dev/bstein/Pegasus/backend/internal"
)

var finalNameRe = regexp.MustCompile(`^\d{4}\.\d{2}\.\d{2}\.[A-Za-z0-9_-]{1,64}(?:\.[A-Za-z0-9_-]{1,64})?\.[A-Za-z0-9]{1,8}$`)
var renameFile = os.Rename

func sanitizeDescriptor(s string) string {
	s = strings.TrimSpace(s)
	s = strings.ReplaceAll(s, " ", "_")
	if s == "" {
		return "upload"
	}

	var b []rune
	lastUnderscore := false
	for _, r := range s {
		ok := (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-'
		if !ok {
			r = '_'
		}
		if r == '_' {
			if lastUnderscore {
				continue
			}
			lastUnderscore = true
		} else {
			lastUnderscore = false
		}
		b = append(b, r)
	if len(b) >= 64 {
		break
	}
}
	return string(b)
}

func stemOf(orig string) string {
	base := strings.TrimSuffix(orig, filepath.Ext(orig))
	s := sanitizeDescriptor(strings.ReplaceAll(base, ".", "_"))
	return s
}

func composeFinalName(date, desc, orig string) string {
	var y, m, d string
	if len(date) >= 10 && date[4] == '-' && date[7] == '-' {
		y, m, d = date[:4], date[5:7], date[8:10]
	} else {
		now := time.Now()
		y = fmt.Sprintf("%04d", now.Year())
		m = fmt.Sprintf("%02d", int(now.Month()))
		d = fmt.Sprintf("%02d", now.Day())
	}
	clean := sanitizeDescriptor(desc)
	stem := stemOf(orig)
	ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(orig), "."))
	if ext == "" {
		ext = "bin"
	}
	return fmt.Sprintf("%s.%s.%s.%s.%s.%s", y, m, d, clean, stem, ext)
}

func moveFromTus(ev handler.HookEvent, dst string) error {
	candidates := []string{
		filepath.Join(tusDir, ev.Upload.ID),
		filepath.Join(tusDir, ev.Upload.ID+".bin"),
		filepath.Join(tusDir, "data", ev.Upload.ID),
		filepath.Join(tusDir, "data", ev.Upload.ID+".bin"),
		filepath.Join(tusDir, "uploads", ev.Upload.ID),
		filepath.Join(tusDir, "uploads", ev.Upload.ID+".bin"),
	}

	var src string
	for _, p := range candidates {
		if fi, err := os.Stat(p); err == nil && fi.Mode().IsRegular() {
			src = p
			break
		}
	}
	if src == "" {
		entries, _ := os.ReadDir(tusDir)
		for _, e := range entries {
			if !e.IsDir() && strings.HasPrefix(e.Name(), ev.Upload.ID) {
				src = filepath.Join(tusDir, e.Name())
				break
			}
		}
	}
	if src == "" {
		return fmt.Errorf("tus data file not found for id %s", ev.Upload.ID)
	}

	if err := os.MkdirAll(filepath.Dir(dst), 0o2775); err != nil {
		return err
	}
	if internal.DryRun {
		internal.Logf("[DRY] mv %s -> %s", src, dst)
		return nil
	}

	if err := renameFile(src, dst); err != nil {
		in, err2 := os.Open(src)
		if err2 != nil {
			return err2
		}
		defer in.Close()

		out, err3 := os.Create(dst)
		if err3 != nil {
			return err3
		}
		if _, err := io.Copy(out, in); err != nil {
			_ = out.Close()
			return err
		}
		_ = out.Close()
		_ = os.Remove(src)
	}

	_ = os.Remove(filepath.Join(tusDir, ev.Upload.ID+".info"))
	_ = os.Remove(filepath.Join(tusDir, ev.Upload.ID+".json"))
	return nil
}
