package server

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/openp2p-cn/inp2p/internal/store"
)

// helpers
func BearerToken(r *http.Request) string {
	h := r.Header.Get("Authorization")
	if h == "" {
		return ""
	}
	parts := strings.SplitN(h, " ", 2)
	if len(parts) != 2 {
		return ""
	}
	if strings.ToLower(parts[0]) != "bearer" {
		return ""
	}
	return strings.TrimSpace(parts[1])
}

func writeJSON(w http.ResponseWriter, status int, body string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	io.WriteString(w, body)
}

func (s *Server) HandleAdminCreateTenant(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodGet {
		tenants, err := s.store.ListTenants()
		if err != nil {
			writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"list tenants failed"}`)
			return
		}
		resp := struct {
			Error   int            `json:"error"`
			Message string         `json:"message"`
			Tenants []store.Tenant `json:"tenants"`
		}{0, "ok", tenants}
		b, _ := json.Marshal(resp)
		writeJSON(w, http.StatusOK, string(b))
		return
	}
	// update tenant status via /api/v1/admin/tenants/{id}?status=0|1
	if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/admin/tenants/") {
		parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
		if len(parts) >= 4 {
			var id int64
			_, _ = fmt.Sscanf(parts[len(parts)-1], "%d", &id)
			st := r.URL.Query().Get("status")
			if id > 0 && st != "" {
				status := 0
				if st == "1" {
					status = 1
				}
				_ = s.store.UpdateTenantStatus(id, status)
				writeJSON(w, http.StatusOK, `{"error":0,"message":"ok"}`)
				return
			}
		}
	}
	if r.Method != http.MethodPost {
		writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`)
		return
	}
	var req struct {
		Name             string `json:"name"`
		AdminPassword    string `json:"admin_password"`
		OperatorPassword string `json:"operator_password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Name == "" {
		writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`)
		return
	}
	if s.store == nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`)
		return
	}
	var ten *store.Tenant
	var admin *store.User
	var op *store.User
	var err error
	if req.AdminPassword != "" && req.OperatorPassword != "" {
		ten, admin, op, err = s.store.CreateTenantWithUsers(req.Name, req.AdminPassword, req.OperatorPassword)
	} else {
		ten, err = s.store.CreateTenant(req.Name)
	}
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create tenant failed"}`)
		return
	}
	resp := struct {
		Error        int    `json:"error"`
		Message      string `json:"message"`
		Tenant       int64  `json:"tenant_id"`
		Subnet       string `json:"subnet"`
		AdminUser    string `json:"admin_user"`
		OperatorUser string `json:"operator_user"`
	}{0, "ok", ten.ID, ten.Subnet, "", ""}
	if admin != nil {
		resp.AdminUser = admin.Email
	}
	if op != nil {
		resp.OperatorUser = op.Email
	}
	b, _ := json.Marshal(resp)
	writeJSON(w, http.StatusOK, string(b))
}

func (s *Server) HandleAdminCreateAPIKey(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost && r.Method != http.MethodGet {
		writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`)
		return
	}
	if s.store == nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`)
		return
	}
	// /api/v1/admin/tenants/{id}/keys
	parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
	if len(parts) < 6 || parts[5] != "keys" {
		writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`)
		return
	}
	// parts: api v1 admin tenants {id} keys
	idPart := parts[4]
	var tenantID int64
	_, _ = fmt.Sscanf(idPart, "%d", &tenantID)
	if tenantID == 0 {
		writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`)
		return
	}
	if r.Method == http.MethodGet {
		keys, err := s.store.ListAPIKeys(tenantID)
		if err != nil {
			writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"list keys failed"}`)
			return
		}
		resp := struct {
			Error   int            `json:"error"`
			Message string         `json:"message"`
			Keys    []store.APIKey `json:"keys"`
		}{0, "ok", keys}
		b, _ := json.Marshal(resp)
		writeJSON(w, http.StatusOK, string(b))
		return
	}
	// update key status via /api/v1/admin/tenants/{id}/keys/{keyId}?status=0|1
	if strings.Contains(r.URL.Path, "/keys/") {
		parts2 := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
		var keyID int64
		_, _ = fmt.Sscanf(parts2[len(parts2)-1], "%d", &keyID)
		st := r.URL.Query().Get("status")
		if keyID > 0 && st != "" {
			status := 0
			if st == "1" {
				status = 1
			}
			_ = s.store.UpdateAPIKeyStatus(keyID, status)
			writeJSON(w, http.StatusOK, `{"error":0,"message":"ok"}`)
			return
		}
	}
	var req struct {
		Scope string `json:"scope"`
		TTL   int64  `json:"ttl"` // seconds
	}
	_ = json.NewDecoder(r.Body).Decode(&req)
	var ttl time.Duration
	if req.TTL > 0 {
		ttl = time.Duration(req.TTL) * time.Second
	}
	key, err := s.store.CreateAPIKey(tenantID, req.Scope, ttl)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create key failed"}`)
		return
	}
	resp := struct {
		Error   int    `json:"error"`
		Message string `json:"message"`
		APIKey  string `json:"api_key"`
		Tenant  int64  `json:"tenant_id"`
	}{0, "ok", key, tenantID}
	b, _ := json.Marshal(resp)
	writeJSON(w, http.StatusOK, string(b))
}

func (s *Server) HandleTenantEnroll(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost && r.Method != http.MethodGet {
		writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`)
		return
	}
	// tenant auth by session/apikey
	if s.store == nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`)
		return
	}
	tok := BearerToken(r)
	ac, ok := s.ResolveTenantAccessToken(tok)
	if !ok || ac.TenantID <= 0 {
		writeJSON(w, http.StatusUnauthorized, `{"error":1,"message":"unauthorized"}`)
		return
	}
	ten, err := s.store.GetTenantByID(ac.TenantID)
	if err != nil || ten == nil || ten.Status != 1 {
		writeJSON(w, http.StatusUnauthorized, `{"error":1,"message":"unauthorized"}`)
		return
	}
	if r.Method == http.MethodGet {
		tokens, err := s.store.ListEnrollTokens(ten.ID)
		if err != nil {
			writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"list enroll failed"}`)
			return
		}
		resp := struct {
			Error   int                 `json:"error"`
			Message string              `json:"message"`
			Enrolls []store.EnrollToken `json:"enrolls"`
		}{0, "ok", tokens}
		b, _ := json.Marshal(resp)
		writeJSON(w, http.StatusOK, string(b))
		return
	}
	code, err := s.store.CreateEnrollToken(ten.ID, 10*time.Minute, 5)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create enroll failed"}`)
		return
	}
	resp := struct {
		Error   int    `json:"error"`
		Message string `json:"message"`
		Code    string `json:"enroll_code"`
		Tenant  int64  `json:"tenant_id"`
	}{0, "ok", code, ten.ID}
	b, _ := json.Marshal(resp)
	writeJSON(w, http.StatusOK, string(b))
}

func (s *Server) HandleEnrollConsume(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeJSON(w, http.StatusMethodNotAllowed, `{"error":1,"message":"method not allowed"}`)
		return
	}
	// revoke support: /api/v1/enroll/consume/{id}?status=0
	if strings.Contains(r.URL.Path, "/enroll/consume/") {
		parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
		if len(parts) >= 4 {
			var id int64
			_, _ = fmt.Sscanf(parts[len(parts)-1], "%d", &id)
			st := r.URL.Query().Get("status")
			if id > 0 && st != "" {
				status := 0
				if st == "1" {
					status = 1
				}
				_ = s.store.UpdateEnrollStatus(id, status)
				writeJSON(w, http.StatusOK, `{"error":0,"message":"ok"}`)
				return
			}
		}
	}
	var req struct {
		Code     string `json:"code"`
		NodeName string `json:"node"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Code == "" || req.NodeName == "" {
		writeJSON(w, http.StatusBadRequest, `{"error":1,"message":"bad request"}`)
		return
	}
	if s.store == nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"store not ready"}`)
		return
	}
	et, err := s.store.ConsumeEnrollToken(req.Code)
	if err != nil {
		s.store.IncEnrollAttempt(req.Code)
		writeJSON(w, http.StatusUnauthorized, `{"error":1,"message":"invalid enroll"}`)
		return
	}
	cred, err := s.store.CreateNodeCredential(et.TenantID, req.NodeName)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, `{"error":1,"message":"create node failed"}`)
		return
	}
	resp := struct {
		Error     int    `json:"error"`
		Message   string `json:"message"`
		NodeID    int64  `json:"node_id"`
		NodeUUID  string `json:"node_uuid"`
		NodeName  string `json:"node_name"`
		Alias     string `json:"alias"`
		VirtualIP string `json:"virtual_ip"`
		Secret    string `json:"node_secret"`
		Tenant    int64  `json:"tenant_id"`
		CreatedAt int64  `json:"created_at"`
	}{0, "ok", cred.NodeID, cred.NodeUUID, cred.NodeName, cred.Alias, cred.VirtualIP, cred.Secret, cred.TenantID, cred.CreatedAt}
	b, _ := json.Marshal(resp)
	writeJSON(w, http.StatusOK, string(b))
}

// placeholder to avoid unused import
var _ = store.Tenant{}
