package handlers

import (
	"encoding/json"
	"log/slog"
	"net/http"

	"github.com/google/uuid"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/auth"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/services"
)

type TenantHandler struct {
	svc *services.TenantService
}

func NewTenantHandler(svc *services.TenantService) *TenantHandler {
	return &TenantHandler{svc: svc}
}

// CreateTenant handles POST /api/tenants
func (h *TenantHandler) CreateTenant(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	var req struct {
		Name string `json:"name"`
		Slug string `json:"slug"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		jsonError(w, "invalid request body", http.StatusBadRequest)
		return
	}
	if req.Name == "" || req.Slug == "" {
		jsonError(w, "name and slug are required", http.StatusBadRequest)
		return
	}

	tenant, err := h.svc.Create(r.Context(), userID, req.Name, req.Slug)
	if err != nil {
		slog.Error("failed to create tenant", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}

	jsonResponse(w, tenant, http.StatusCreated)
}

// ListTenants handles GET /api/tenants
func (h *TenantHandler) ListTenants(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	tenants, err := h.svc.ListForUser(r.Context(), userID)
	if err != nil {
		slog.Error("failed to list tenants", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}

	// Mask CalDAV passwords in tenant settings
	for i := range tenants {
		tenants[i].Settings = maskSettingsPassword(tenants[i].Settings)
	}

	jsonResponse(w, tenants, http.StatusOK)
}

// GetTenant handles GET /api/tenants/{id}
func (h *TenantHandler) GetTenant(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	tenantID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		jsonError(w, "invalid tenant ID", http.StatusBadRequest)
		return
	}

	// Verify user has access to this tenant
	role, err := h.svc.GetUserRole(r.Context(), userID, tenantID)
	if err != nil {
		slog.Error("failed to get user role", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}
	if role == "" {
		jsonError(w, "not found", http.StatusNotFound)
		return
	}

	tenant, err := h.svc.GetByID(r.Context(), tenantID)
	if err != nil {
		slog.Error("failed to get tenant", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}
	if tenant == nil {
		jsonError(w, "not found", http.StatusNotFound)
		return
	}

	// Mask CalDAV password before returning
	tenant.Settings = maskSettingsPassword(tenant.Settings)

	jsonResponse(w, tenant, http.StatusOK)
}

// InviteUser handles POST /api/tenants/{id}/invite
func (h *TenantHandler) InviteUser(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	tenantID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		jsonError(w, "invalid tenant ID", http.StatusBadRequest)
		return
	}

	// Only owners and partners can invite
	role, err := h.svc.GetUserRole(r.Context(), userID, tenantID)
	if err != nil {
		slog.Error("failed to get user role", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}
	if role != "owner" && role != "partner" {
		jsonError(w, "only owners and partners can invite users", http.StatusForbidden)
		return
	}

	var req struct {
		Email string `json:"email"`
		Role  string `json:"role"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		jsonError(w, "invalid request body", http.StatusBadRequest)
		return
	}
	if req.Email == "" {
		jsonError(w, "email is required", http.StatusBadRequest)
		return
	}
	if req.Role == "" {
		req.Role = "associate"
	}
	if !auth.IsValidRole(req.Role) {
		jsonError(w, "invalid role", http.StatusBadRequest)
		return
	}
	// Non-owners cannot invite as owner
	if role != "owner" && req.Role == "owner" {
		jsonError(w, "only owners can invite as owner", http.StatusForbidden)
		return
	}

	ut, err := h.svc.InviteByEmail(r.Context(), tenantID, req.Email, req.Role)
	if err != nil {
		// These are user-facing validation errors (user not found, already member)
		jsonError(w, "failed to invite user", http.StatusBadRequest)
		return
	}

	jsonResponse(w, ut, http.StatusCreated)
}

// RemoveMember handles DELETE /api/tenants/{id}/members/{uid}
func (h *TenantHandler) RemoveMember(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	tenantID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		jsonError(w, "invalid tenant ID", http.StatusBadRequest)
		return
	}

	memberID, err := uuid.Parse(r.PathValue("uid"))
	if err != nil {
		jsonError(w, "invalid member ID", http.StatusBadRequest)
		return
	}

	// Only owners and partners can remove members (or user removing themselves)
	role, err := h.svc.GetUserRole(r.Context(), userID, tenantID)
	if err != nil {
		slog.Error("failed to get user role", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}
	if role != "owner" && role != "partner" && userID != memberID {
		jsonError(w, "insufficient permissions", http.StatusForbidden)
		return
	}

	if err := h.svc.RemoveMember(r.Context(), tenantID, memberID); err != nil {
		// These are user-facing validation errors (not a member, last owner, etc.)
		jsonError(w, "failed to remove member", http.StatusBadRequest)
		return
	}

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

// UpdateSettings handles PUT /api/tenants/{id}/settings
func (h *TenantHandler) UpdateSettings(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	tenantID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		jsonError(w, "invalid tenant ID", http.StatusBadRequest)
		return
	}

	// Only owners and partners can update settings
	role, err := h.svc.GetUserRole(r.Context(), userID, tenantID)
	if err != nil {
		slog.Error("failed to get user role", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}
	if role != "owner" && role != "partner" {
		jsonError(w, "only owners and partners can update settings", http.StatusForbidden)
		return
	}

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

	tenant, err := h.svc.UpdateSettings(r.Context(), tenantID, settings)
	if err != nil {
		slog.Error("failed to update settings", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}

	// Mask CalDAV password before returning
	tenant.Settings = maskSettingsPassword(tenant.Settings)

	jsonResponse(w, tenant, http.StatusOK)
}

// ListMembers handles GET /api/tenants/{id}/members
func (h *TenantHandler) ListMembers(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	tenantID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		jsonError(w, "invalid tenant ID", http.StatusBadRequest)
		return
	}

	// Verify user has access
	role, err := h.svc.GetUserRole(r.Context(), userID, tenantID)
	if err != nil {
		slog.Error("failed to get user role", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}
	if role == "" {
		jsonError(w, "not found", http.StatusNotFound)
		return
	}

	members, err := h.svc.ListMembers(r.Context(), tenantID)
	if err != nil {
		slog.Error("failed to list members", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}

	jsonResponse(w, members, http.StatusOK)
}

// UpdateMemberRole handles PUT /api/tenants/{id}/members/{uid}/role
func (h *TenantHandler) UpdateMemberRole(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	tenantID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		jsonError(w, "invalid tenant ID", http.StatusBadRequest)
		return
	}

	memberID, err := uuid.Parse(r.PathValue("uid"))
	if err != nil {
		jsonError(w, "invalid member ID", http.StatusBadRequest)
		return
	}

	// Only owners and partners can change roles
	role, err := h.svc.GetUserRole(r.Context(), userID, tenantID)
	if err != nil {
		jsonError(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if role != "owner" && role != "partner" {
		jsonError(w, "only owners and partners can change roles", http.StatusForbidden)
		return
	}

	var req struct {
		Role string `json:"role"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		jsonError(w, "invalid request body", http.StatusBadRequest)
		return
	}
	if !auth.IsValidRole(req.Role) {
		jsonError(w, "invalid role", http.StatusBadRequest)
		return
	}

	// Non-owners cannot promote to owner
	if role != "owner" && req.Role == "owner" {
		jsonError(w, "only owners can promote to owner", http.StatusForbidden)
		return
	}

	if err := h.svc.UpdateMemberRole(r.Context(), tenantID, memberID, req.Role); err != nil {
		jsonError(w, err.Error(), http.StatusBadRequest)
		return
	}

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

// AutoAssign handles POST /api/tenants/auto-assign — checks if the user's email domain
// matches any tenant's auto_assign_domains and assigns them if so.
func (h *TenantHandler) AutoAssign(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	var req struct {
		Email string `json:"email"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		jsonError(w, "invalid request body", http.StatusBadRequest)
		return
	}
	if req.Email == "" {
		jsonError(w, "email is required", http.StatusBadRequest)
		return
	}

	// Extract domain from email
	parts := splitEmail(req.Email)
	if parts == "" {
		jsonError(w, "invalid email format", http.StatusBadRequest)
		return
	}

	result, err := h.svc.AutoAssignByDomain(r.Context(), userID, parts)
	if err != nil {
		slog.Error("auto-assign failed", "error", err)
		jsonError(w, "internal error", http.StatusInternalServerError)
		return
	}

	if result == nil {
		jsonResponse(w, map[string]any{"assigned": false}, http.StatusOK)
		return
	}

	jsonResponse(w, map[string]any{
		"assigned":  true,
		"tenant_id": result.ID,
		"name":      result.Name,
		"slug":      result.Slug,
		"role":      result.Role,
		"settings":  result.Settings,
	}, http.StatusOK)
}

// splitEmail extracts the domain part from an email address.
func splitEmail(email string) string {
	at := -1
	for i, c := range email {
		if c == '@' {
			at = i
			break
		}
	}
	if at < 0 || at >= len(email)-1 {
		return ""
	}
	return email[at+1:]
}

// GetMe handles GET /api/me — returns the current user's ID and role in the active tenant.
func (h *TenantHandler) GetMe(w http.ResponseWriter, r *http.Request) {
	userID, ok := auth.UserFromContext(r.Context())
	if !ok {
		http.Error(w, "unauthorized", http.StatusUnauthorized)
		return
	}

	role := auth.UserRoleFromContext(r.Context())
	tenantID, _ := auth.TenantFromContext(r.Context())

	// Get user's permissions for frontend UI
	perms := auth.GetRolePermissions(role)

	// Check if tenant is in demo mode
	isDemo := false
	if tenant, err := h.svc.GetByID(r.Context(), tenantID); err == nil && tenant != nil {
		var settings map[string]json.RawMessage
		if json.Unmarshal(tenant.Settings, &settings) == nil {
			if demoRaw, ok := settings["demo"]; ok {
				var demo bool
				if json.Unmarshal(demoRaw, &demo) == nil {
					isDemo = demo
				}
			}
		}
	}

	jsonResponse(w, map[string]any{
		"user_id":     userID,
		"tenant_id":   tenantID,
		"role":        role,
		"permissions": perms,
		"is_demo":     isDemo,
	}, http.StatusOK)
}

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

func jsonError(w http.ResponseWriter, msg string, status int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(map[string]string{"error": msg})
}
