package infra

import (
	"crypto/rand"
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"time"
)

// sessionEnsureTable crea la tabla sessions si no existe.
// Columnas: token (PK), user_id, expires_at, created_at, metadata (JSON).
func sessionEnsureTable(db *sql.DB) error {
	const stmt = `
CREATE TABLE IF NOT EXISTS sessions (
    token      TEXT PRIMARY KEY,
    user_id    TEXT NOT NULL,
    expires_at INTEGER NOT NULL,
    created_at INTEGER NOT NULL,
    metadata   TEXT NOT NULL DEFAULT '{}'
);
CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
`
	_, err := db.Exec(stmt)
	return err
}

// SessionCreate genera un token aleatorio de 32 bytes (64 hex chars) e
// inserta la sesion en la tabla sessions. Crea la tabla si no existe.
// Retorna la Session completa lista para devolver al cliente.
func SessionCreate(db *sql.DB, userID string, ttl time.Duration, metadata map[string]any) (Session, error) {
	var zero Session
	if db == nil {
		return zero, fmt.Errorf("session_create: db nil")
	}
	if userID == "" {
		return zero, fmt.Errorf("session_create: user_id vacio")
	}
	if ttl <= 0 {
		return zero, fmt.Errorf("session_create: ttl debe ser positivo")
	}
	if err := sessionEnsureTable(db); err != nil {
		return zero, fmt.Errorf("session_create: crear tabla: %w", err)
	}

	raw := make([]byte, 32)
	if _, err := rand.Read(raw); err != nil {
		return zero, fmt.Errorf("session_create: rand: %w", err)
	}
	token := hex.EncodeToString(raw)

	now := time.Now().Unix()
	exp := now + int64(ttl.Seconds())

	var metaJSON []byte
	if metadata == nil {
		metaJSON = []byte("{}")
	} else {
		b, err := json.Marshal(metadata)
		if err != nil {
			return zero, fmt.Errorf("session_create: marshal metadata: %w", err)
		}
		metaJSON = b
	}

	_, err := db.Exec(
		"INSERT INTO sessions (token, user_id, expires_at, created_at, metadata) VALUES (?, ?, ?, ?, ?)",
		token, userID, exp, now, string(metaJSON),
	)
	if err != nil {
		return zero, fmt.Errorf("session_create: insert: %w", err)
	}

	return Session{
		Token:     token,
		UserID:    userID,
		ExpiresAt: exp,
		CreatedAt: now,
		Metadata:  metadata,
	}, nil
}
