package main

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"log"
	"net/http"
	"strings"
	"time"
)

func randomToken(n int) string {
	b := make([]byte, n)
	rand.Read(b)
	return base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(b)
}

func jsonResp(w http.ResponseWriter, status int, v any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(v)
}

func jsonErr(w http.ResponseWriter, status int, msg string) {
	jsonResp(w, status, map[string]string{"error": msg})
}

// Get email from session cookie
func authEmail(r *http.Request) string {
	c, err := r.Cookie("v1984_session")
	if err != nil {
		return ""
	}
	email, err := sessionGet(c.Value)
	if err != nil {
		return ""
	}
	return email
}

func setSessionCookie(w http.ResponseWriter, token string) {
	http.SetCookie(w, &http.Cookie{
		Name:     "v1984_session",
		Value:    token,
		Path:     "/",
		HttpOnly: true,
		Secure:   true,
		SameSite: http.SameSiteLaxMode,
		MaxAge:   86400,
	})
}

func clearSessionCookie(w http.ResponseWriter) {
	http.SetCookie(w, &http.Cookie{
		Name:     "v1984_session",
		Value:    "",
		Path:     "/",
		HttpOnly: true,
		Secure:   true,
		SameSite: http.SameSiteLaxMode,
		MaxAge:   -1,
	})
}

// POST /api/auth/email — send login code (stubbed: always 123456)
func apiAuthEmail(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		jsonErr(w, 405, "method not allowed")
		return
	}
	var req struct {
		Email string `json:"email"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Email == "" {
		jsonErr(w, 400, "email required")
		return
	}
	email := strings.ToLower(strings.TrimSpace(req.Email))

	// Auto-create account if it doesn't exist
	exists, _, _ := accountGet(email)
	if !exists {
		if err := accountCreate(email); err != nil {
			log.Printf("account auto-create error: %v", err)
			jsonErr(w, 500, "internal error")
			return
		}
		log.Printf("account auto-created: %s", email)
	}

	// Store code (stub: always 123456)
	code := "123456"
	if err := loginCodeSet(email, code); err != nil {
		log.Printf("login code error: %v", err)
		jsonErr(w, 500, "internal error")
		return
	}

	log.Printf("login code for %s: %s (stub — not emailed)", email, code)
	jsonResp(w, 200, map[string]string{"status": "sent"})
}

// POST /api/auth/verify — verify login code
func apiAuthVerify(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		jsonErr(w, 405, "method not allowed")
		return
	}
	var req struct {
		Email string `json:"email"`
		Code  string `json:"code"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Email == "" || req.Code == "" {
		jsonErr(w, 400, "email and code required")
		return
	}
	email := strings.ToLower(strings.TrimSpace(req.Email))

	ok, err := loginCodeVerify(email, req.Code)
	if err != nil || !ok {
		jsonErr(w, 401, "invalid or expired code")
		return
	}

	token, err := sessionCreate(email)
	if err != nil {
		jsonErr(w, 500, "internal error")
		return
	}

	setSessionCookie(w, token)
	jsonResp(w, 200, map[string]string{"status": "ok"})
}

// POST /api/auth/logout
func apiAuthLogout(w http.ResponseWriter, r *http.Request) {
	if c, err := r.Cookie("v1984_session"); err == nil {
		sessionDelete(c.Value)
	}
	clearSessionCookie(w)
	jsonResp(w, 200, map[string]string{"status": "ok"})
}

// POST /api/checkout — create account (stub: no Stripe yet)
func apiCheckout(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		jsonErr(w, 405, "method not allowed")
		return
	}
	var req struct {
		Email string `json:"email"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Email == "" {
		jsonErr(w, 400, "email required")
		return
	}
	email := strings.ToLower(strings.TrimSpace(req.Email))

	// Create account (idempotent)
	if err := accountCreate(email); err != nil {
		log.Printf("account create error: %v", err)
		jsonErr(w, 500, "internal error")
		return
	}

	// Auto-login after checkout (stub — real flow goes through Stripe first)
	token, err := sessionCreate(email)
	if err != nil {
		jsonErr(w, 500, "internal error")
		return
	}
	setSessionCookie(w, token)

	log.Printf("account created (stub): %s", email)
	// In production: return Stripe checkout URL
	// For now: redirect to regions page
	jsonResp(w, 200, map[string]string{"url": basePath + "/regions"})
}

// GET /api/vaults — list vaults for authenticated user
func apiVaults(w http.ResponseWriter, r *http.Request) {
	email := authEmail(r)
	if email == "" {
		jsonErr(w, 401, "not authenticated")
		return
	}
	vaults, err := vaultList(email)
	if err != nil {
		jsonErr(w, 500, "internal error")
		return
	}
	if vaults == nil {
		vaults = []Vault{}
	}
	count, _ := vaultCount(email)
	jsonResp(w, 200, map[string]any{
		"email":    email,
		"vaults":   vaults,
		"count":    count,
		"max":      1, // consumer: 1 vault
	})
}

// POST /api/vault/create
func apiVaultCreate(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		jsonErr(w, 405, "method not allowed")
		return
	}
	email := authEmail(r)
	if email == "" {
		jsonErr(w, 401, "not authenticated")
		return
	}
	var req struct {
		VaultID string `json:"vault_id"`
		Region  string `json:"region"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Region == "" {
		jsonErr(w, 400, "region required")
		return
	}

	// Check capacity
	count, _ := vaultCount(email)
	if count >= 1 {
		jsonErr(w, 409, "vault limit reached")
		return
	}

	// Generate vault ID if not provided (normally comes from PRF on the POP)
	vaultID := req.VaultID
	if vaultID == "" {
		vaultID = randomToken(4)[:6] // 6 chars, stub
	}

	if err := vaultCreate(vaultID, email, req.Region); err != nil {
		log.Printf("vault create error: %v", err)
		jsonErr(w, 500, "internal error")
		return
	}

	log.Printf("vault created: %s for %s in %s", vaultID, email, req.Region)
	jsonResp(w, 201, map[string]string{
		"vault_id": vaultID,
		"region":   req.Region,
		"expires_at": time.Now().AddDate(1, 0, 0).UTC().Format(time.RFC3339),
	})
}

// POST /api/vault/{id}/delete
func apiVaultDelete(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		jsonErr(w, 405, "method not allowed")
		return
	}
	email := authEmail(r)
	if email == "" {
		jsonErr(w, 401, "not authenticated")
		return
	}

	// Extract vault ID from path: /api/vault/{id}/delete
	parts := strings.Split(r.URL.Path, "/")
	var vaultID string
	for i, p := range parts {
		if p == "vault" && i+1 < len(parts) {
			vaultID = parts[i+1]
			break
		}
	}
	if vaultID == "" {
		jsonErr(w, 400, "vault_id required")
		return
	}

	if err := vaultDelete(vaultID, email); err != nil {
		jsonErr(w, 404, "vault not found")
		return
	}

	log.Printf("vault deleted: %s for %s", vaultID, email)
	jsonResp(w, 200, map[string]string{"status": "deleted"})
}

// GET /api/account — account info
func apiAccount(w http.ResponseWriter, r *http.Request) {
	email := authEmail(r)
	if email == "" {
		jsonErr(w, 401, "not authenticated")
		return
	}
	exists, stripeID, _ := accountGet(email)
	if !exists {
		jsonErr(w, 404, "account not found")
		return
	}
	count, _ := vaultCount(email)
	jsonResp(w, 200, map[string]any{
		"email":     email,
		"stripe_id": stripeID,
		"vaults":    count,
		"max":       1,
	})
}
