package com.cameleer.server.app.alerting.controller;

import com.cameleer.server.app.alerting.dto.AlertDto;
import com.cameleer.server.app.alerting.dto.BulkIdsRequest;
import com.cameleer.server.app.alerting.dto.UnreadCountResponse;
import com.cameleer.server.app.alerting.notify.InAppInboxQuery;
import com.cameleer.server.app.web.EnvPath;
import com.cameleer.server.core.alerting.AlertInstance;
import com.cameleer.server.core.alerting.AlertInstanceRepository;
import com.cameleer.server.core.alerting.AlertSeverity;
import com.cameleer.server.core.alerting.AlertState;
import com.cameleer.server.core.runtime.Environment;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;

import java.time.Instant;
import java.util.List;
import java.util.UUID;

/**
 * REST controller for the in-app alert inbox (env-scoped).
 * VIEWER+ can read their own inbox; OPERATOR+ can soft-delete and restore alerts.
 */
@RestController
@RequestMapping("/api/v1/environments/{envSlug}/alerts")
@Tag(name = "Alerts Inbox", description = "In-app alert inbox, ack and read tracking (env-scoped)")
@PreAuthorize("hasAnyRole('VIEWER','OPERATOR','ADMIN')")
public class AlertController {

    private final InAppInboxQuery inboxQuery;
    private final AlertInstanceRepository instanceRepo;

    public AlertController(InAppInboxQuery inboxQuery,
                           AlertInstanceRepository instanceRepo) {
        this.inboxQuery = inboxQuery;
        this.instanceRepo = instanceRepo;
    }

    @GetMapping
    public List<AlertDto> list(
            @EnvPath Environment env,
            @RequestParam(defaultValue = "50") int limit,
            @RequestParam(required = false) List<AlertState> state,
            @RequestParam(required = false) List<AlertSeverity> severity,
            @RequestParam(required = false) Boolean acked,
            @RequestParam(required = false) Boolean read) {
        String userId = currentUserId();
        int effectiveLimit = Math.min(limit, 200);
        return inboxQuery.listInbox(env.id(), userId, state, severity, acked, read, effectiveLimit)
                .stream().map(AlertDto::from).toList();
    }

    @GetMapping("/unread-count")
    public UnreadCountResponse unreadCount(@EnvPath Environment env) {
        return inboxQuery.countUnread(env.id(), currentUserId());
    }

    @GetMapping("/{id}")
    public AlertDto get(@EnvPath Environment env, @PathVariable UUID id) {
        AlertInstance instance = requireLiveInstance(id, env.id());
        return AlertDto.from(instance);
    }

    @PostMapping("/{id}/ack")
    public AlertDto ack(@EnvPath Environment env, @PathVariable UUID id) {
        AlertInstance instance = requireLiveInstance(id, env.id());
        String userId = currentUserId();
        instanceRepo.ack(id, userId, Instant.now());
        // Re-fetch to return fresh state
        return AlertDto.from(instanceRepo.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)));
    }

    @PostMapping("/{id}/read")
    public void read(@EnvPath Environment env, @PathVariable UUID id) {
        requireLiveInstance(id, env.id());
        instanceRepo.markRead(id, Instant.now());
    }

    @PostMapping("/bulk-read")
    public void bulkRead(@EnvPath Environment env,
                         @Valid @RequestBody BulkIdsRequest req) {
        List<UUID> filtered = inEnvLiveIds(req.instanceIds(), env.id());
        if (!filtered.isEmpty()) {
            instanceRepo.bulkMarkRead(filtered, Instant.now());
        }
    }

    @PostMapping("/bulk-ack")
    public void bulkAck(@EnvPath Environment env,
                        @Valid @RequestBody BulkIdsRequest req) {
        List<UUID> filtered = inEnvLiveIds(req.instanceIds(), env.id());
        if (!filtered.isEmpty()) {
            instanceRepo.bulkAck(filtered, currentUserId(), Instant.now());
        }
    }

    @DeleteMapping("/{id}")
    @PreAuthorize("hasAnyRole('OPERATOR','ADMIN')")
    public ResponseEntity<Void> delete(@EnvPath Environment env, @PathVariable UUID id) {
        requireLiveInstance(id, env.id());
        instanceRepo.softDelete(id, Instant.now());
        return ResponseEntity.noContent().build();
    }

    @PostMapping("/bulk-delete")
    @PreAuthorize("hasAnyRole('OPERATOR','ADMIN')")
    public void bulkDelete(@EnvPath Environment env,
                           @Valid @RequestBody BulkIdsRequest req) {
        List<UUID> filtered = inEnvLiveIds(req.instanceIds(), env.id());
        if (!filtered.isEmpty()) {
            instanceRepo.bulkSoftDelete(filtered, Instant.now());
        }
    }

    @PostMapping("/{id}/restore")
    @PreAuthorize("hasAnyRole('OPERATOR','ADMIN')")
    public ResponseEntity<Void> restore(@EnvPath Environment env, @PathVariable UUID id) {
        // Unlike requireLiveInstance, restore explicitly targets soft-deleted rows
        AlertInstance inst = instanceRepo.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Alert not found"));
        if (!inst.environmentId().equals(env.id()))
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Alert not found in env");
        instanceRepo.restore(id);
        return ResponseEntity.noContent().build();
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private AlertInstance requireLiveInstance(UUID id, UUID envId) {
        AlertInstance i = instanceRepo.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Alert not found"));
        if (!i.environmentId().equals(envId) || i.deletedAt() != null)
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Alert not found in env");
        return i;
    }

    private List<UUID> inEnvLiveIds(List<UUID> ids, UUID envId) {
        return instanceRepo.filterInEnvLive(ids, envId);
    }

    private String currentUserId() {
        var auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth == null || auth.getName() == null) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "No authentication");
        }
        String name = auth.getName();
        return name.startsWith("user:") ? name.substring(5) : name;
    }
}
