package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/lib/pq"
	"golang.org/x/crypto/bcrypt"

	"doctopus/auth"
	"doctopus/handlers"
)

var db *sql.DB

func main() {
	dsn := fmt.Sprintf(
		"host=%s port=5432 user=%s password=%s dbname=%s sslmode=disable",
		os.Getenv("DB_HOST"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME"),
	)
	conn, err := sql.Open("postgres", dsn)
	if err != nil {
		panic(err)
	}
	if err := conn.Ping(); err != nil {
		panic(err)
	}
	db = conn

	// Admin-Benutzer erstellen (nur einmal)
	if err := auth.CreateAdminUser(db); err != nil {
		// Ignoriere "duplicate key" Fehler, falls der Benutzer schon existiert
		if !isDuplicateKeyError(err) {
			panic(err)
		}
	}

	// JWT Secret aus Env
	secret := os.Getenv("JWT_SECRET")
	if secret == "" {
		panic("JWT_SECRET nicht gesetzt")
	}
	auth.SetSecret(secret)

	// HTTP-Routen
	// Health
	http.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) {
		json.NewEncoder(w).Encode(map[string]string{
			"instance": os.Getenv("INSTANCE_NAME"),
		})
	})

	// Auth
	http.HandleFunc("/auth/login", login)
	http.HandleFunc("/auth/refresh", refresh)
	http.HandleFunc("/auth/logout", logout)

	// Protected API (with JWT Middleware)
	http.Handle("/api/documents",
		auth.Middleware(handlers.DocumentsHandler(db)),
	)

	http.Handle("/api/documents/",
		auth.Middleware(handlers.DocumentsHandler(db)),
	)

	fmt.Println("Server läuft auf :3000")
	http.ListenAndServe(":3000", nil)
}

func login(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "bad request", http.StatusBadRequest)
		return
	}

	var hash string
	if err := db.QueryRow("SELECT password_hash FROM users WHERE username=$1", req.Username).Scan(&hash); err != nil {
		http.Error(w, "invalid username or password", http.StatusUnauthorized)
		return
	}

	if bcrypt.CompareHashAndPassword([]byte(hash), []byte(req.Password)) != nil {
		http.Error(w, "invalid username or password", http.StatusUnauthorized)
		return
	}

	accessToken, err := auth.GenerateAccessToken(req.Username)
	if err != nil {
		http.Error(w, "token error", http.StatusInternalServerError)
		return
	}

	refreshToken, err := auth.GenerateRefreshToken(req.Username)
	if err != nil {
		http.Error(w, "token error", http.StatusInternalServerError)
		return
	}

	// Cookie setzen
	secure := false
	if os.Getenv("ENV") == "production" {
		secure = true // Nur in Produktion HTTPS erzwingen
	}

	http.SetCookie(w, &http.Cookie{
		Name:     "access_token",
		Value:    accessToken,
		Path:     "/",
		HttpOnly: true,
		Secure:   secure,
		SameSite: http.SameSiteStrictMode,
	})

	http.SetCookie(w, &http.Cookie{
		Name:     "refresh_token",
		Value:    refreshToken,
		Path:     "/",
		HttpOnly: true,
		Secure:   secure,
		SameSite: http.SameSiteStrictMode,
	})

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"status": "logged in"})
}

func refresh(w http.ResponseWriter, r *http.Request) {
	cookie, err := r.Cookie("refresh_token")
	if err != nil {
		http.Error(w, "no refresh token", http.StatusUnauthorized)
		return
	}

	claims, err := auth.VerifyToken(cookie.Value)
	if err != nil {
		http.Error(w, "invalid refresh token", http.StatusUnauthorized)
		return
	}

	// Sicherheit: Überprüfe dass es ein refresh_token ist
	if claims.Type != "refresh" {
		http.Error(w, "invalid token type", http.StatusUnauthorized)
		return
	}

	accessToken, err := auth.GenerateAccessToken(claims.User)
	if err != nil {
		http.Error(w, "token error", http.StatusInternalServerError)
		return
	}

	secure := os.Getenv("ENV") == "production"

	http.SetCookie(w, &http.Cookie{
		Name:     "access_token",
		Value:    accessToken,
		Path:     "/",
		HttpOnly: true,
		Secure:   secure,
		SameSite: http.SameSiteStrictMode,
	})

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"status": "token refreshed"})
}

func logout(w http.ResponseWriter, r *http.Request) {
	secure := os.Getenv("ENV") == "production"

	// Clear access_token cookie
	http.SetCookie(w, &http.Cookie{
		Name:     "access_token",
		Value:    "",
		Path:     "/",
		HttpOnly: true,
		Secure:   secure,
		SameSite: http.SameSiteStrictMode,
		MaxAge:   -1, // Delete cookie
	})

	// Clear refresh_token cookie
	http.SetCookie(w, &http.Cookie{
		Name:     "refresh_token",
		Value:    "",
		Path:     "/",
		HttpOnly: true,
		Secure:   secure,
		SameSite: http.SameSiteStrictMode,
		MaxAge:   -1, // Delete cookie
	})

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"status": "logged out"})
}

func isDuplicateKeyError(err error) bool {
	// Postgres unique violation: SQLSTATE 23505
	pqErr, ok := err.(*pq.Error)
	return ok && pqErr.Code == "23505"
}
