package http

import (
	"encoding/json"
	"net/http"
	"strings"

	"go.b0esche.cloud/backend/internal/audit"
	"go.b0esche.cloud/backend/internal/auth"
	"go.b0esche.cloud/backend/internal/config"
	"go.b0esche.cloud/backend/internal/database"
	"go.b0esche.cloud/backend/internal/middleware"
	"go.b0esche.cloud/backend/internal/org"
	"go.b0esche.cloud/backend/internal/permission"
	"go.b0esche.cloud/backend/pkg/jwt"

	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"
)

func NewRouter(cfg *config.Config, db *database.DB, jwtManager *jwt.Manager, authService *auth.Service, auditLogger *audit.Logger) http.Handler {
	r := chi.NewRouter()

	// Global middleware
	r.Use(middleware.RequestID)
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)
	r.Use(middleware.RateLimit)

	// Health check
	r.Get("/health", healthHandler)

	// Auth routes (no auth required)
	r.Route("/auth", func(r chi.Router) {
		r.Get("/login", func(w http.ResponseWriter, req *http.Request) {
			authLoginHandler(w, req, authService)
		})
		r.Get("/callback", func(w http.ResponseWriter, req *http.Request) {
			authCallbackHandler(w, req, cfg, authService, jwtManager, auditLogger, db)
		})
		r.Post("/refresh", func(w http.ResponseWriter, req *http.Request) {
			refreshHandler(w, req, jwtManager, db)
		})
	})

	// Auth middleware for protected routes
	r.Use(middleware.Auth(jwtManager, db))

	// Org routes
	r.Get("/orgs", func(w http.ResponseWriter, req *http.Request) {
		listOrgsHandler(w, req, db, jwtManager)
	})
	r.Post("/orgs", func(w http.ResponseWriter, req *http.Request) {
		createOrgHandler(w, req, db, auditLogger, jwtManager)
	})

	// Org-scoped routes
	r.Route("/orgs/{orgId}", func(r chi.Router) {
		r.Use(middleware.Org(db, auditLogger))

		// File routes
		r.With(middleware.Permission(db, auditLogger, permission.FileRead)).Get("/files", func(w http.ResponseWriter, req *http.Request) {
			listFilesHandler(w, req)
		})
		r.Route("/files/{fileId}", func(r chi.Router) {
			r.With(middleware.Permission(db, auditLogger, permission.DocumentView)).Get("/view", func(w http.ResponseWriter, req *http.Request) {
				viewerHandler(w, req, db, auditLogger)
			})
			r.With(middleware.Permission(db, auditLogger, permission.DocumentEdit)).Get("/edit", func(w http.ResponseWriter, req *http.Request) {
				editorHandler(w, req, db, auditLogger)
			})
			r.With(middleware.Permission(db, auditLogger, permission.DocumentEdit)).Post("/annotations", func(w http.ResponseWriter, req *http.Request) {
				annotationsHandler(w, req, db, auditLogger)
			})
			r.Get("/meta", func(w http.ResponseWriter, req *http.Request) {
				fileMetaHandler(w, req)
			})
		})
		r.Get("/activity", func(w http.ResponseWriter, req *http.Request) {
			activityHandler(w, req, db)
		})
		r.With(middleware.Permission(db, auditLogger, permission.OrgManage)).Get("/members", func(w http.ResponseWriter, req *http.Request) {
			listMembersHandler(w, req, db)
		})
		r.With(middleware.Permission(db, auditLogger, permission.OrgManage)).Patch("/members/{userId}", func(w http.ResponseWriter, req *http.Request) {
			updateMemberRoleHandler(w, req, db, auditLogger)
		})
	})

	return r
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}

func authLoginHandler(w http.ResponseWriter, r *http.Request, authService *auth.Service) {
	state, err := auth.GenerateState()
	if err != nil {
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}

	// TODO: Store state securely (e.g., in session or cache)

	url := authService.LoginURL(state)
	http.Redirect(w, r, url, http.StatusFound)
}

func authCallbackHandler(w http.ResponseWriter, r *http.Request, cfg *config.Config, authService *auth.Service, jwtManager *jwt.Manager, auditLogger *audit.Logger, db *database.DB) {
	code := r.URL.Query().Get("code")
	state := r.URL.Query().Get("state")

	// TODO: Validate state

	user, session, err := authService.HandleCallback(r.Context(), code, state)
	if err != nil {
		auditLogger.Log(r.Context(), audit.Entry{
			Action:   "login",
			Success:  false,
			Metadata: map[string]interface{}{"error": err.Error()},
		})
		http.Error(w, "Authentication failed", http.StatusUnauthorized)
		return
	}

	// Get user orgs
	orgs, err := org.ResolveUserOrgs(r.Context(), db, user.ID)
	if err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}
	orgIDs := make([]string, len(orgs))
	for i, o := range orgs {
		orgIDs[i] = o.ID.String()
	}

	token, err := jwtManager.Generate(user.Email, orgIDs, session.ID.String())
	if err != nil {
		http.Error(w, "Token generation failed", http.StatusInternalServerError)
		return
	}

	auditLogger.Log(r.Context(), audit.Entry{
		UserID:  &user.ID,
		Action:  "login",
		Success: true,
	})

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(`{"token": "` + token + `"}`))
}

func refreshHandler(w http.ResponseWriter, r *http.Request, jwtManager *jwt.Manager, db *database.DB) {
	authHeader := r.Header.Get("Authorization")
	if !strings.HasPrefix(authHeader, "Bearer ") {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}
	tokenString := strings.TrimPrefix(authHeader, "Bearer ")

	claims, session, err := jwtManager.ValidateWithSession(r.Context(), tokenString, db)
	if err != nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	userID, _ := uuid.Parse(claims.UserID)
	orgs, err := db.GetUserOrganizations(r.Context(), userID)
	if err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}
	orgIDs := make([]string, len(orgs))
	for i, o := range orgs {
		orgIDs[i] = o.ID.String()
	}

	newToken, err := jwtManager.Generate(claims.UserID, orgIDs, session.ID.String())
	if err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(`{"token": "` + newToken + `"}`))
}

func listOrgsHandler(w http.ResponseWriter, r *http.Request, db *database.DB, jwtManager *jwt.Manager) {
	authHeader := r.Header.Get("Authorization")
	if !strings.HasPrefix(authHeader, "Bearer ") {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}
	tokenString := strings.TrimPrefix(authHeader, "Bearer ")

	claims, _, err := jwtManager.ValidateWithSession(r.Context(), tokenString, db)
	if err != nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	userID, _ := uuid.Parse(claims.UserID)
	orgs, err := org.ResolveUserOrgs(r.Context(), db, userID)
	if err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(orgs)
}

func createOrgHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger, jwtManager *jwt.Manager) {
	authHeader := r.Header.Get("Authorization")
	if !strings.HasPrefix(authHeader, "Bearer ") {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}
	tokenString := strings.TrimPrefix(authHeader, "Bearer ")

	claims, _, err := jwtManager.ValidateWithSession(r.Context(), tokenString, db)
	if err != nil {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	userID, _ := uuid.Parse(claims.UserID)

	var req struct {
		Name string `json:"name"`
		Slug string `json:"slug,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	org, err := org.CreateOrg(r.Context(), db, userID, req.Name, req.Slug)
	if err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}

	auditLogger.Log(r.Context(), audit.Entry{
		UserID:  &userID,
		OrgID:   &org.ID,
		Action:  "create_org",
		Success: true,
	})

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(org)
}

func listFilesHandler(w http.ResponseWriter, r *http.Request) {
	// Mock files
	files := []struct {
		Name         string `json:"name"`
		Path         string `json:"path"`
		Type         string `json:"type"`
		Size         int    `json:"size"`
		LastModified string `json:"lastModified"`
	}{
		{"test.pdf", "/test.pdf", "file", 1234, "2023-01-01T00:00:00Z"},
		{"folder", "/folder", "folder", 0, "2023-01-01T00:00:00Z"},
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(files)
}

func viewerHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
	userIDStr := r.Context().Value("user").(string)
	userID, _ := uuid.Parse(userIDStr)
	orgID := r.Context().Value("org").(uuid.UUID)
	fileId := chi.URLParam(r, "fileId")

	// Log activity
	db.LogActivity(r.Context(), userID, orgID, &fileId, "view_file", map[string]interface{}{})

	session := struct {
		ViewUrl      string `json:"viewUrl"`
		Capabilities struct {
			CanEdit    bool `json:"canEdit"`
			CanAnnotate bool `json:"canAnnotate"`
			IsPdf      bool `json:"isPdf"`
		} `json:"capabilities"`
		ExpiresAt string `json:"expiresAt"`
	}{
		ViewUrl: "https://view.example.com/" + fileId,
		Capabilities: struct {
			CanEdit    bool `json:"canEdit"`
			CanAnnotate bool `json:"canAnnotate"`
			IsPdf      bool `json:"isPdf"`
		}{CanEdit: true, CanAnnotate: true, IsPdf: true},
		ExpiresAt: "2023-01-01T01:00:00Z",
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(session)
}

func editorHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
	userIDStr := r.Context().Value("user").(string)
	userID, _ := uuid.Parse(userIDStr)
	orgID := r.Context().Value("org").(uuid.UUID)
	fileId := chi.URLParam(r, "fileId")

	// Log activity
	db.LogActivity(r.Context(), userID, orgID, &fileId, "edit_file", map[string]interface{}{})

	session := struct {
		EditUrl   string `json:"editUrl"`
		ReadOnly  bool   `json:"readOnly"`
		ExpiresAt string `json:"expiresAt"`
	}{
		EditUrl:   "https://edit.example.com/" + fileId,
		ReadOnly:  false,
		ExpiresAt: "2023-01-01T01:00:00Z",
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(session)
}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(session)
}

func annotationsHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
	userIDStr := r.Context().Value("user").(string)
	userID, _ := uuid.Parse(userIDStr)
	orgID := r.Context().Value("org").(uuid.UUID)
	fileId := chi.URLParam(r, "fileId")

	// Parse payload
	var payload struct {
		Annotations   []interface{} `json:"annotations"`
		BaseVersionId string        `json:"baseVersionId"`
	}
	if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
		http.Error(w, "Bad request", http.StatusBadRequest)
		return
	}

	// Log activity
	auditLogger.Log(r.Context(), audit.Entry{
		UserID:   &userID,
		OrgID:    &orgID,
		Resource: &fileId,
		Action:   "annotate_pdf",
		Success:  true,
		Metadata: map[string]interface{}{"count": len(payload.Annotations)},
	})

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"status": "ok"}`))
}

func activityHandler(w http.ResponseWriter, r *http.Request, db *database.DB) {
	orgID := r.Context().Value("org").(uuid.UUID)

	activities, err := db.GetOrgActivities(r.Context(), orgID, 50)
	if err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(activities)
}

func listMembersHandler(w http.ResponseWriter, r *http.Request, db *database.DB) {
	orgID := r.Context().Value("org").(uuid.UUID)

	members, err := db.GetOrgMembers(r.Context(), orgID)
	if err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(members)
}

func updateMemberRoleHandler(w http.ResponseWriter, r *http.Request, db *database.DB, auditLogger *audit.Logger) {
	orgID := r.Context().Value("org").(uuid.UUID)
	userIDStr := chi.URLParam(r, "userId")
	userID, err := uuid.Parse(userIDStr)
	if err != nil {
		http.Error(w, "Invalid user ID", http.StatusBadRequest)
		return
	}

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

	if err := db.UpdateMemberRole(r.Context(), orgID, userID, req.Role); err != nil {
		http.Error(w, "Server error", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"status": "ok"}`))
}

func fileMetaHandler(w http.ResponseWriter, r *http.Request) {
	meta := struct {
		LastModified   string `json:"lastModified"`
		LastModifiedBy string `json:"lastModifiedBy"`
		VersionCount   int    `json:"versionCount"`
	}{
		LastModified:   "2023-01-01T00:00:00Z",
		LastModifiedBy: "user@example.com",
		VersionCount:   1,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(meta)
}
