package handlers

import (
	"database/sql"
	"encoding/json"
	"errors"
	"net/http"

	"github.com/google/uuid"
)

// GET /api/projects/{id}/team — returns direct + inherited team members.
// inherited=true rows include inherited_from_id / inherited_from_title.
func handleListProjectTeam(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"})
		return
	}
	rows, err := dbSvc.team.ListEffectiveMembers(r.Context(), uid, id)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, rows)
}

// POST /api/projects/{id}/team — add a direct member.
// Body: {"user_id": "<uuid>", "responsibility": "<lead|member|observer|external>"}
//
// Legacy clients that submit `role` are still accepted as a synonym
// during the deprecation window; the field is treated as a
// responsibility value when the new field is absent.
func handleAddProjectTeamMember(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	projectID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"})
		return
	}
	var body struct {
		UserID         uuid.UUID `json:"user_id"`
		Responsibility string    `json:"responsibility"`
		// Legacy field, accepted for one release while frontend migrates.
		Role string `json:"role"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	resp := body.Responsibility
	if resp == "" {
		resp = body.Role
	}
	m, err := dbSvc.team.AddMember(r.Context(), uid, projectID, body.UserID, resp)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, m)
}

// GET /api/team/memberships — bulk index of project_teams membership for
// every (visible) user × project pair. Powers the /team page project-
// multi-select filter (t-paliad-147 / issue #7). Cheap to call: one
// scan per call; client-side filter handles everything from there.
func handleListMembershipsIndex(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	rows, err := dbSvc.team.ListMembershipsIndex(r.Context(), uid)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, rows)
}

// DELETE /api/projects/{id}/team/{user_id} — remove a direct member.
// Inherited memberships can't be removed at the child level.
func handleRemoveProjectTeamMember(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	projectID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid project id"})
		return
	}
	userID, err := uuid.Parse(r.PathValue("user_id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid user id"})
		return
	}
	if err := dbSvc.team.RemoveMember(r.Context(), uid, projectID, userID); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			writeJSON(w, http.StatusNotFound, map[string]string{
				"error": "no direct membership — inherited memberships must be removed at the ancestor",
			})
			return
		}
		writeServiceError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
