package com.cameleer.server.app.controller;

import com.cameleer.server.core.rbac.ClaimMappingRepository;
import com.cameleer.server.core.rbac.ClaimMappingRule;
import com.cameleer.server.core.rbac.ClaimMappingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/api/v1/admin/claim-mappings")
@PreAuthorize("hasRole('ADMIN')")
@Tag(name = "Claim Mapping Admin", description = "Manage OIDC claim-to-role/group mapping rules")
public class ClaimMappingAdminController {

    private final ClaimMappingRepository repository;
    private final ClaimMappingService claimMappingService;

    public ClaimMappingAdminController(ClaimMappingRepository repository,
                                       ClaimMappingService claimMappingService) {
        this.repository = repository;
        this.claimMappingService = claimMappingService;
    }

    @GetMapping
    @Operation(summary = "List all claim mapping rules")
    public List<ClaimMappingRule> list() {
        return repository.findAll();
    }

    @GetMapping("/{id}")
    @Operation(summary = "Get a claim mapping rule by ID")
    public ResponseEntity<ClaimMappingRule> get(@PathVariable UUID id) {
        return repository.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    record CreateRuleRequest(String claim, String matchType, String matchValue,
                             String action, String target, int priority) {}

    @PostMapping
    @Operation(summary = "Create a claim mapping rule")
    public ResponseEntity<ClaimMappingRule> create(@RequestBody CreateRuleRequest request) {
        UUID id = repository.create(
                request.claim(), request.matchType(), request.matchValue(),
                request.action(), request.target(), request.priority());
        return repository.findById(id)
                .map(rule -> ResponseEntity.created(URI.create("/api/v1/admin/claim-mappings/" + id)).body(rule))
                .orElse(ResponseEntity.internalServerError().build());
    }

    @PutMapping("/{id}")
    @Operation(summary = "Update a claim mapping rule")
    public ResponseEntity<ClaimMappingRule> update(@PathVariable UUID id, @RequestBody CreateRuleRequest request) {
        if (repository.findById(id).isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        repository.update(id, request.claim(), request.matchType(), request.matchValue(),
                request.action(), request.target(), request.priority());
        return repository.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.internalServerError().build());
    }

    @DeleteMapping("/{id}")
    @Operation(summary = "Delete a claim mapping rule")
    public ResponseEntity<Void> delete(@PathVariable UUID id) {
        if (repository.findById(id).isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        repository.delete(id);
        return ResponseEntity.noContent().build();
    }

    record MatchedRuleResponse(String ruleId, int priority, String claim, String matchType,
                               String matchValue, String action, String target) {}

    record TestResponse(List<MatchedRuleResponse> matchedRules, List<String> effectiveRoles,
                        List<String> effectiveGroups, boolean fallback) {}

    record TestRuleRequest(String id, String claim, String matchType, String matchValue,
                           String action, String target, int priority) {}

    record TestRequest(List<TestRuleRequest> rules, Map<String, Object> claims) {}

    @PostMapping("/test")
    @Operation(summary = "Test claim mapping rules against a set of claims (accepts unsaved rules)")
    public TestResponse test(@RequestBody TestRequest request) {
        // Build a lookup from synthetic UUID → original string ID (supports temp- prefixed IDs)
        Map<UUID, String> idLookup = new HashMap<>();
        List<ClaimMappingRule> rules = request.rules().stream()
                .map(r -> {
                    UUID uuid = UUID.randomUUID();
                    idLookup.put(uuid, r.id());
                    return new ClaimMappingRule(uuid, r.claim(), r.matchType(), r.matchValue(),
                            r.action(), r.target(), r.priority(), null);
                })
                .toList();

        List<ClaimMappingService.MappingResult> results = claimMappingService.evaluate(rules, request.claims());

        List<MatchedRuleResponse> matched = results.stream()
                .map(r -> new MatchedRuleResponse(
                        idLookup.get(r.rule().id()), r.rule().priority(), r.rule().claim(),
                        r.rule().matchType(), r.rule().matchValue(),
                        r.rule().action(), r.rule().target()))
                .toList();

        List<String> effectiveRoles = results.stream()
                .filter(r -> "assignRole".equals(r.rule().action()))
                .map(r -> r.rule().target())
                .distinct()
                .toList();

        List<String> effectiveGroups = results.stream()
                .filter(r -> "addToGroup".equals(r.rule().action()))
                .map(r -> r.rule().target())
                .distinct()
                .toList();

        return new TestResponse(matched, effectiveRoles, effectiveGroups, results.isEmpty());
    }
}
