package auth

import (
	"context"
	"net/http"

	"github.com/google/uuid"
	"github.com/jmoiron/sqlx"
)

// Valid roles ordered by privilege level (highest first).
var ValidRoles = []string{"owner", "partner", "associate", "paralegal", "secretary"}

// IsValidRole checks if a role string is one of the defined roles.
func IsValidRole(role string) bool {
	for _, r := range ValidRoles {
		if r == role {
			return true
		}
	}
	return false
}

// Permission represents an action that can be checked against roles.
type Permission int

const (
	PermManageTeam Permission = iota
	PermManageBilling
	PermCreateCase
	PermEditAllCases
	PermEditAssignedCase
	PermViewAllCases
	PermManageDeadlines
	PermManageAppointments
	PermUploadDocuments
	PermDeleteDocuments
	PermDeleteOwnDocuments
	PermViewAuditLog
	PermManageSettings
	PermAIExtraction
)

// rolePermissions maps each role to its set of permissions.
var rolePermissions = map[string]map[Permission]bool{
	"owner": {
		PermManageTeam:         true,
		PermManageBilling:      true,
		PermCreateCase:         true,
		PermEditAllCases:       true,
		PermEditAssignedCase:   true,
		PermViewAllCases:       true,
		PermManageDeadlines:    true,
		PermManageAppointments: true,
		PermUploadDocuments:    true,
		PermDeleteDocuments:    true,
		PermDeleteOwnDocuments: true,
		PermViewAuditLog:       true,
		PermManageSettings:     true,
		PermAIExtraction:       true,
	},
	"partner": {
		PermManageTeam:         true,
		PermManageBilling:      true,
		PermCreateCase:         true,
		PermEditAllCases:       true,
		PermEditAssignedCase:   true,
		PermViewAllCases:       true,
		PermManageDeadlines:    true,
		PermManageAppointments: true,
		PermUploadDocuments:    true,
		PermDeleteDocuments:    true,
		PermDeleteOwnDocuments: true,
		PermViewAuditLog:       true,
		PermManageSettings:     true,
		PermAIExtraction:       true,
	},
	"associate": {
		PermCreateCase:         true,
		PermEditAssignedCase:   true,
		PermViewAllCases:       true,
		PermManageDeadlines:    true,
		PermManageAppointments: true,
		PermUploadDocuments:    true,
		PermDeleteOwnDocuments: true,
		PermAIExtraction:       true,
	},
	"paralegal": {
		PermEditAssignedCase:   true,
		PermViewAllCases:       true,
		PermManageDeadlines:    true,
		PermManageAppointments: true,
		PermUploadDocuments:    true,
	},
	"secretary": {
		PermViewAllCases:       true,
		PermManageAppointments: true,
		PermUploadDocuments:    true,
	},
}

// HasPermission checks if the given role has the specified permission.
func HasPermission(role string, perm Permission) bool {
	perms, ok := rolePermissions[role]
	if !ok {
		return false
	}
	return perms[perm]
}

// RequirePermission returns middleware that checks if the user's role has the given permission.
func RequirePermission(perm Permission) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			role := UserRoleFromContext(r.Context())
			if role == "" || !HasPermission(role, perm) {
				writeJSONError(w, "insufficient permissions", http.StatusForbidden)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}

// RequireRole returns middleware that checks if the user has one of the specified roles.
func RequireRole(roles ...string) func(http.Handler) http.Handler {
	allowed := make(map[string]bool, len(roles))
	for _, r := range roles {
		allowed[r] = true
	}
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			role := UserRoleFromContext(r.Context())
			if !allowed[role] {
				writeJSONError(w, "insufficient permissions", http.StatusForbidden)
				return
			}
			next.ServeHTTP(w, r)
		})
	}
}

// IsAssignedToCase checks if a user is assigned to a specific case.
func IsAssignedToCase(ctx context.Context, db *sqlx.DB, userID, caseID uuid.UUID) (bool, error) {
	var exists bool
	err := db.GetContext(ctx, &exists,
		`SELECT EXISTS(SELECT 1 FROM case_assignments WHERE user_id = $1 AND case_id = $2)`,
		userID, caseID)
	return exists, err
}

// CanEditCase checks if a user can edit a specific case based on role and assignment.
func CanEditCase(ctx context.Context, db *sqlx.DB, userID, caseID uuid.UUID, role string) (bool, error) {
	// Owner and partner can edit all cases
	if HasPermission(role, PermEditAllCases) {
		return true, nil
	}
	// Others need to be assigned
	if !HasPermission(role, PermEditAssignedCase) {
		return false, nil
	}
	return IsAssignedToCase(ctx, db, userID, caseID)
}

// CanDeleteDocument checks if a user can delete a specific document.
func CanDeleteDocument(role string, docUploaderID, userID uuid.UUID) bool {
	if HasPermission(role, PermDeleteDocuments) {
		return true
	}
	if HasPermission(role, PermDeleteOwnDocuments) {
		return docUploaderID == userID
	}
	return false
}

// permissionNames maps Permission constants to their string names for frontend use.
var permissionNames = map[Permission]string{
	PermManageTeam:         "manage_team",
	PermManageBilling:      "manage_billing",
	PermCreateCase:         "create_case",
	PermEditAllCases:       "edit_all_cases",
	PermEditAssignedCase:   "edit_assigned_case",
	PermViewAllCases:       "view_all_cases",
	PermManageDeadlines:    "manage_deadlines",
	PermManageAppointments: "manage_appointments",
	PermUploadDocuments:    "upload_documents",
	PermDeleteDocuments:    "delete_documents",
	PermDeleteOwnDocuments: "delete_own_documents",
	PermViewAuditLog:       "view_audit_log",
	PermManageSettings:     "manage_settings",
	PermAIExtraction:       "ai_extraction",
}

// GetRolePermissions returns a list of permission name strings for the given role.
func GetRolePermissions(role string) []string {
	perms, ok := rolePermissions[role]
	if !ok {
		return nil
	}
	var names []string
	for p := range perms {
		if name, ok := permissionNames[p]; ok {
			names = append(names, name)
		}
	}
	return names
}

func writeJSONError(w http.ResponseWriter, msg string, status int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	w.Write([]byte(`{"error":"` + msg + `"}`))
}
