package com.walkguide.controller;

import com.walkguide.dto.ApiResponse;
import com.walkguide.dto.request.CallNotifyRequest;
import com.walkguide.dto.request.CallTokenRequest;
import com.walkguide.dto.response.AgoraTokenResponse;
import com.walkguide.entity.User;
import com.walkguide.exception.ResourceNotFoundException;
import com.walkguide.repository.UserRepository;
import com.walkguide.security.SecurityHelper;
import com.walkguide.service.AgoraTokenService;
import com.walkguide.service.FcmService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * Controller untuk VoIP call via Agora RTC.
 *
 * FLOW:
 *   1. Caller → POST /shared/call/token   → dapat Agora token + channelName
 *   2. Caller join Agora channel (di Flutter)
 *   3. Caller → POST /shared/call/notify  → FCM "Incoming Call" dikirim ke receiver
 *   4. Receiver terima FCM → join channel yang sama
 *   5. Audio call tersambung via Agora
 *
 * Route: /api/v1/shared/call
 * Auth: Authenticated (Guardian ATAU User)
 */
@RestController
@RequestMapping("/api/v1/shared/call")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "Call", description = "VoIP call via Agora RTC")
@SecurityRequirement(name = "bearerAuth")
public class CallController {

    private final AgoraTokenService agoraTokenService;
    private final FcmService fcmService;
    private final UserRepository userRepository;

    /**
     * Generate Agora RTC token untuk call session.
     * Dipanggil oleh CALLER sebelum mulai call.
     *
     * POST /api/v1/shared/call/token
     */
    @PostMapping("/token")
    @Operation(summary = "Generate Agora token", description = "Caller minta token sebelum join Agora channel")
    public ResponseEntity<ApiResponse<AgoraTokenResponse>> generateToken(
            @Valid @RequestBody CallTokenRequest req) {

        Long callerId = SecurityHelper.getCurrentUserId();
        AgoraTokenResponse response = agoraTokenService.generateToken(callerId, req.getReceiverId());

        log.info("[CALL] Token generated | caller={} receiver={} channel={}",
                callerId, req.getReceiverId(), response.getChannelName());

        return ResponseEntity.ok(ApiResponse.ok(response, "Token Agora berhasil digenerate"));
    }

    /**
     * Kirim FCM "Incoming Call" ke receiver.
     * Dipanggil oleh CALLER setelah berhasil join Agora channel.
     *
     * POST /api/v1/shared/call/notify
     */
    @PostMapping("/notify")
    @Operation(summary = "Notify receiver of incoming call",
               description = "Kirim FCM push notification ke receiver agar join Agora channel yang sama")
    public ResponseEntity<ApiResponse<Void>> notifyCall(
            @Valid @RequestBody CallNotifyRequest req) {

        Long callerId = SecurityHelper.getCurrentUserId();

        // Ambil info caller untuk notifikasi
        User caller = userRepository.findById(callerId)
                .orElseThrow(() -> new ResourceNotFoundException("Caller not found"));

        // Ambil FCM token receiver
        User receiver = userRepository.findById(req.getReceiverId())
                .orElseThrow(() -> new ResourceNotFoundException("Receiver not found"));

        if (receiver.getFcmToken() == null || receiver.getFcmToken().isBlank()) {
            log.warn("[CALL] Receiver {} tidak punya FCM token — call notify gagal", req.getReceiverId());
            return ResponseEntity.ok(ApiResponse.ok(null,
                    "Panggilan dikirim (receiver mungkin tidak menerima push notification)"));
        }

        // Payload FCM untuk incoming call
        Map<String, String> payload = Map.of(
                "type",        "INCOMING_CALL",
                "callerId",    String.valueOf(callerId),
                "callerName",  caller.getDisplayName() != null ? caller.getDisplayName() : caller.getEmail(),
                "channelName", req.getChannelName(),
                "agoraToken",  req.getAgoraToken() != null ? req.getAgoraToken() : "",
                "receiverUid", String.valueOf(req.getReceiverUid())
        );

        // High priority karena incoming call harus segera muncul
        fcmService.sendHighPriority(
                receiver.getFcmToken(),
                "📞 Panggilan Masuk",
                "Panggilan dari " + (caller.getDisplayName() != null ? caller.getDisplayName() : caller.getEmail()),
                payload
        );

        log.info("[CALL] Incoming call notification sent | caller={} receiver={} channel={}",
                callerId, req.getReceiverId(), req.getChannelName());

        return ResponseEntity.ok(ApiResponse.ok(null, "Notifikasi panggilan berhasil dikirim"));
    }

    /**
     * End call — notifikasi ke pihak lain bahwa call sudah berakhir.
     * Opsional: Flutter bisa handle end call secara lokal juga.
     *
     * POST /api/v1/shared/call/end
     */
    @PostMapping("/end")
    @Operation(summary = "Notify end of call")
    public ResponseEntity<ApiResponse<Void>> endCall(
            @RequestBody Map<String, Long> body) {

        Long callerId  = SecurityHelper.getCurrentUserId();
        Long otherId   = body.get("otherId");

        if (otherId != null) {
            userRepository.findById(otherId).ifPresent(other -> {
                if (other.getFcmToken() != null && !other.getFcmToken().isBlank()) {
                    fcmService.sendToToken(
                            other.getFcmToken(),
                            "Panggilan Berakhir",
                            "Panggilan telah berakhir",
                            Map.of("type", "CALL_ENDED", "callerId", String.valueOf(callerId))
                    );
                }
            });
        }

        return ResponseEntity.ok(ApiResponse.ok(null, "Call ended"));
    }
}
