package org.raddatz.familienarchiv.person.relationship;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.raddatz.familienarchiv.person.Person;
import org.raddatz.familienarchiv.person.relationship.dto.RelationshipUpsertRequest;
import org.raddatz.familienarchiv.person.relationship.dto.FamilyMemberPatchDTO;
import org.raddatz.familienarchiv.person.relationship.dto.InferredRelationshipDTO;
import org.raddatz.familienarchiv.person.relationship.dto.InferredRelationshipWithPersonDTO;
import org.raddatz.familienarchiv.person.relationship.dto.NetworkDTO;
import org.raddatz.familienarchiv.person.relationship.dto.RelationshipDTO;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.security.Permission;
import org.raddatz.familienarchiv.security.RequirePermission;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

/**
 * Stammbaum API. Endpoints split across two roots:
 * <ul>
 *   <li>{@code /api/network} — the family graph</li>
 *   <li>{@code /api/persons/{id}/...} — per-person relationship operations
 *       (PersonController is intentionally left untouched)</li>
 * </ul>
 */
@RestController
@RequiredArgsConstructor
public class RelationshipController {

    private final RelationshipService relationshipService;

    // READ endpoints carry no @RequirePermission: all authenticated users may read the family graph.
    // Unauthenticated requests are rejected by Spring Security's anyRequest().authenticated() rule.

    @GetMapping("/api/network")
    public NetworkDTO getNetwork() {
        return relationshipService.getFamilyNetwork();
    }

    @GetMapping("/api/persons/{id}/relationships")
    public List<RelationshipDTO> getRelationships(@PathVariable UUID id) {
        return relationshipService.getRelationships(id);
    }

    @GetMapping("/api/persons/{id}/inferred-relationships")
    public List<InferredRelationshipWithPersonDTO> getInferredRelationships(@PathVariable UUID id) {
        return relationshipService.getInferredRelationships(id);
    }

    @GetMapping("/api/persons/{aId}/relationship-to/{bId}")
    public InferredRelationshipDTO getRelationshipBetween(@PathVariable UUID aId, @PathVariable UUID bId) {
        return relationshipService.getRelationshipBetween(aId, bId)
                .orElseThrow(() -> DomainException.notFound(
                        ErrorCode.RELATIONSHIP_NOT_FOUND, "No relationship path between " + aId + " and " + bId));
    }

    @PostMapping("/api/persons/{id}/relationships")
    @RequirePermission(Permission.WRITE_ALL)
    public ResponseEntity<RelationshipDTO> addRelationship(
            @PathVariable UUID id,
            @Valid @RequestBody RelationshipUpsertRequest dto) {
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(relationshipService.addRelationship(id, dto));
    }

    @PutMapping("/api/persons/{id}/relationships/{relId}")
    @RequirePermission(Permission.WRITE_ALL)
    public RelationshipDTO updateRelationship(
            @PathVariable UUID id,
            @PathVariable UUID relId,
            @Valid @RequestBody RelationshipUpsertRequest dto) {
        return relationshipService.updateRelationship(id, relId, dto);
    }

    @DeleteMapping("/api/persons/{id}/relationships/{relId}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    @RequirePermission(Permission.WRITE_ALL)
    public void deleteRelationship(@PathVariable UUID id, @PathVariable UUID relId) {
        relationshipService.deleteRelationship(id, relId);
    }

    @PatchMapping("/api/persons/{id}/family-member")
    @RequirePermission(Permission.WRITE_ALL)
    public Person patchFamilyMember(@PathVariable UUID id, @RequestBody FamilyMemberPatchDTO dto) {
        return relationshipService.setFamilyMember(id, dto.familyMember());
    }

}
