package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"archivmail/internal/audit"
	"archivmail/internal/auth"
)

// apiKeyCreateRequest is the JSON body for POST /api/admin/apikeys.
type apiKeyCreateRequest struct {
	Name      string `json:"name"`
	Role      string `json:"role"`
	RateLimit int    `json:"rate_limit"`
}

// handleCreateAPIKey generates a new API key for the current tenant.
// POST /api/admin/apikeys
func (s *Server) handleCreateAPIKey(w http.ResponseWriter, r *http.Request) {
	sess := sessionFromCtx(r.Context())
	tenantID := tenantFromCtx(r.Context())

	var req apiKeyCreateRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	if req.Name == "" {
		writeError(w, http.StatusBadRequest, "name is required")
		return
	}

	// Validate role.
	if req.Role == "" {
		req.Role = "user"
	}
	if req.Role != "user" && req.Role != "auditor" {
		writeError(w, http.StatusBadRequest, "role must be 'user' or 'auditor'")
		return
	}

	if req.RateLimit <= 0 {
		req.RateLimit = 60
	}
	if req.RateLimit > 1000 {
		req.RateLimit = 1000
	}

	// Generate key.
	rawToken, tokenHash, err := auth.GenerateAPIKey()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to generate API key")
		return
	}

	// Determine tenant_id.
	var tid int64
	if tenantID != nil {
		tid = *tenantID
	}
	if tid == 0 {
		writeError(w, http.StatusBadRequest, "API keys require a tenant context")
		return
	}

	// Insert into DB.
	var keyID int64
	row := s.store.DBQueryRow(r.Context(),
		`INSERT INTO api_keys (tenant_id, name, token_hash, role, rate_limit)
		 VALUES ($1, $2, $3, $4, $5)
		 RETURNING id`,
		tid, req.Name, tokenHash, req.Role, req.RateLimit,
	)
	if err := row.Scan(&keyID); err != nil {
		s.logger.Error("create api key failed", "err", err)
		writeError(w, http.StatusInternalServerError, "failed to create API key")
		return
	}

	// Audit log.
	s.audlog.Log(audit.Entry{
		EventType: audit.EventUserMgmt,
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		Detail:    fmt.Sprintf("created api key %q (id=%d, role=%s)", req.Name, keyID, req.Role),
		Success:   true,
	})

	// Return the raw token ONCE.
	writeJSON(w, http.StatusCreated, map[string]interface{}{
		"id":         keyID,
		"name":       req.Name,
		"role":       req.Role,
		"rate_limit": req.RateLimit,
		"token":      rawToken,
		"message":    "Save this token now. It will not be shown again.",
	})
}

// handleListAPIKeys lists API keys for the current tenant.
// GET /api/admin/apikeys
func (s *Server) handleListAPIKeys(w http.ResponseWriter, r *http.Request) {
	tenantID := tenantFromCtx(r.Context())
	var tid int64
	if tenantID != nil {
		tid = *tenantID
	}

	rows, err := s.store.DBQuery(r.Context(),
		`SELECT id, name, role, active, rate_limit, created_at, last_used_at
		 FROM api_keys
		 WHERE tenant_id = $1
		 ORDER BY created_at DESC`,
		tid,
	)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to list API keys")
		return
	}
	defer rows.Close()

	type apiKeyResponse struct {
		ID         int64   `json:"id"`
		Name       string  `json:"name"`
		Role       string  `json:"role"`
		Active     bool    `json:"active"`
		RateLimit  int     `json:"rate_limit"`
		CreatedAt  string  `json:"created_at"`
		LastUsedAt *string `json:"last_used_at"`
	}

	keys := make([]apiKeyResponse, 0)
	for rows.Next() {
		var k apiKeyResponse
		var createdAt time.Time
		var lastUsedAt *time.Time
		if err := rows.Scan(&k.ID, &k.Name, &k.Role, &k.Active, &k.RateLimit, &createdAt, &lastUsedAt); err != nil {
			continue
		}
		k.CreatedAt = createdAt.UTC().Format(time.RFC3339)
		if lastUsedAt != nil {
			s := lastUsedAt.UTC().Format(time.RFC3339)
			k.LastUsedAt = &s
		}
		keys = append(keys, k)
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"api_keys": keys,
	})
}

// handleDeleteAPIKey deletes an API key belonging to the current tenant.
// DELETE /api/admin/apikeys/{id}
func (s *Server) handleDeleteAPIKey(w http.ResponseWriter, r *http.Request) {
	sess := sessionFromCtx(r.Context())
	tenantID := tenantFromCtx(r.Context())

	idStr := r.PathValue("id")
	keyID, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid key id")
		return
	}

	var tid int64
	if tenantID != nil {
		tid = *tenantID
	}

	// Delete only if it belongs to this tenant.
	tag, err := s.store.DBExec(r.Context(),
		`DELETE FROM api_keys WHERE id = $1 AND tenant_id = $2`,
		keyID, tid,
	)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to delete API key")
		return
	}
	if tag == 0 {
		writeError(w, http.StatusNotFound, "API key not found")
		return
	}

	// Audit log.
	s.audlog.Log(audit.Entry{
		EventType: audit.EventUserMgmt,
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		Detail:    fmt.Sprintf("deleted api key id=%d", keyID),
		Success:   true,
	})

	writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}
