package com.its.onvif.cmra.server.controller;

import com.its.onvif.cmra.server.dto.request.PtzControlRequestDto;
import com.its.onvif.cmra.server.dto.PtzDirection;
import com.its.onvif.cmra.server.dto.response.*;
import com.its.onvif.cmra.server.service.OnvifCameraService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.util.EnumMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

@RequiredArgsConstructor
@Tag(name = "Onvif IP Camera Control API", description = "ONVIF 기반의 IP 카메라 제어 및 조회 기능을 제공합니다.")
@RestController
@RequestMapping("/api/camera")
public class OnvifCameraController {

    private final OnvifCameraService service;

//    private final EnumMap<PtzDirection, Consumer<PtzControlRequestDto>> ptzCommandMap = new EnumMap<>(PtzDirection.class);
//
//    @PostConstruct
//    private void initPtzCommandMap() {
//        this.ptzCommandMap.put(null, ctx -> stop(ctx));
//        this.ptzCommandMap.put(PtzDirection.PAN_LEFT, ctx -> panLeft(ctx));
//        this.ptzCommandMap.put(PtzDirection.PAN_RIGHT, ctx -> panRight(ctx));
//        this.ptzCommandMap.put(PtzDirection.TILT_UP, ctx -> tiltUp(ctx));
//        this.ptzCommandMap.put(PtzDirection.TILT_DOWN, ctx -> tiltDown(ctx));
//        this.ptzCommandMap.put(PtzDirection.PAN_TILT_UP_LEFT, ctx -> panTiltUpLeft(ctx));
//        this.ptzCommandMap.put(PtzDirection.PAN_TILT_UP_RIGHT, ctx -> panTiltUpRight(ctx));
//        this.ptzCommandMap.put(PtzDirection.PAN_TILT_DOWN_LEFT, ctx -> panTiltDownLeft(ctx));
//        this.ptzCommandMap.put(PtzDirection.PAN_TILT_DOWN_RIGHT, ctx -> panTiltDownRight(ctx));
//        this.ptzCommandMap.put(PtzDirection.ZOOM_IN, ctx -> zoomIn(ctx));
//        this.ptzCommandMap.put(PtzDirection.ZOOM_OUT, ctx -> zoomOut(ctx));
//        this.ptzCommandMap.put(PtzDirection.HOME, ctx -> moveHome(ctx));
//    }
//
//    private PtzControlResultDto stop(PtzControlRequestDto ctx) {
//        return stop(ctx.getIp(), ctx.getPort(), ctx.getUsername(), ctx.getPassword());
//    }

    @GetMapping("/info")
    @Operation(
            summary = "카메라 디바이스 정보 조회",
            description = "제조사, 모델명, 펌웨어버전, 호스트명, 제공 중인 모든 스트림 정보 반환",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = CameraDeviceInfoDto.class)
                            )
                    ),
//                    @ApiResponse(responseCode = "401", description = "인증 실패"),
//                    @ApiResponse(responseCode = "500", description = "서버 오류")
            }
    )
    public CameraDeviceInfoDto getCameraInfo(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password
    ) throws Exception {
        return this.service.getDeviceInfo(ip + ":" + port, username, password);
    }

    @GetMapping("/streams")
    @Operation(
            summary = "카메라 스트림 목록 조회",
            description = "모든 프로파일에 대한 스트림 URI 및 인코딩/해상도/프레임레이트 등 반환",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = CameraStreamProfileDto.class)
                            )
                    ),
            }
    )
    public List<CameraStreamProfileDto> getStreamProfiles(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password
    ) throws Exception {
        return this.service.getStreamProfiles(ip + ":" + port, username, password);
    }


    // PTZ 상태 조회
    @GetMapping("/ptz/status")
    @Operation(
            summary = "PTZ 상태 조회",
            description = "현재 pan/tilt/zoom 위치를 조회합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzStatusDto.class)
                            )
                    ),
            }
    )
    public PtzStatusDto getStatus(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password
    ) throws Exception {
        return this.service.getStatus(ip + ":" + port, username, password);
    }

    /// PTZ 제어
    @PostMapping("/ptz/stop")
    @Operation(
            summary = "STOP",
            description = "현재 PTZ 이동을 정지시킵니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto stop(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password
    ) {
        String result = "SUCCESS";
        String message = "STOP";
        try {
            this.service.stop(ip + ":" + port, username, password);
        } catch (Exception e) {
            result = "FAIL";
            message = e.getMessage();
        }
        return PtzControlResultDto.builder()
                .success(result)
                .message(message)
                .build();
    }

    @PostMapping("/ptz/control")
    @Operation(
            summary = "PTZ 제어",
            description = "카메라 이동/줌/홈 위치로 제어",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto ptzControl(
            @RequestBody PtzControlRequestDto req
    ) {
        PtzDirection dir = PtzDirection.fromTypeOrStop(req.getType());  // type=0 → dir == null
//        this.ptzCommandMap.get(dir).accept(req); // null 키에 stop() 미리 매핑돼 있음

//        Consumer<PtzControlRequestDto> action = ptzCommandMap.get(dir);
//        if (action == null) {
//            return PtzControlResultDto.builder()
//                    .success("FAIL")
//                    .message("정의되지 않은 제어 동작입니다.")
//                    .build();
//        }
//        action.accept(req);

        String ip = req.getIp();
        int port = Optional.ofNullable(req.getPort()).orElse(80);
        String user = req.getUsername();
        String pass = req.getPassword();
        int speedPercent = Optional.ofNullable(req.getSpeed()).orElse(30);
        float scaledSpeed = Math.max(1, Math.min(speedPercent, 100)) / 100.0f;

        if (req.getType() == 0) return stop(ip, port, user, pass);

        try {
//            PtzDirection dir = PtzDirection.fromType(req.getType());
            this.service.move(ip + ":" + port, user, pass, dir, scaledSpeed);
            return PtzControlResultDto.builder()
                    .success("SUCCESS")
                    .message(dir.getDescription() + " (속도: " + speedPercent + "%)")
                    .build();

        } catch (IllegalArgumentException e) {
            return PtzControlResultDto.builder()
                    .success("FAIL")
                    .message("요청 오류: " + e.getMessage())
                    .build();

        } catch (Exception e) {
            return PtzControlResultDto.builder()
                    .success("FAIL")
                    .message("PTZ 제어 중 오류 발생: " + e.getMessage())
                    .build();
        }
    }

    @PostMapping("/ptz/left")
    @Operation(
            summary = "PAN_LEFT",
            description = "카메라를 좌측(PAN_LEFT)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto panLeft(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.PAN_LEFT, speed);
    }

    @PostMapping("/ptz/right")
    @Operation(
            summary = "PAN_RIGHT",
            description = "카메라를 우측(PAN_RIGHT)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto panRight(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.PAN_RIGHT, speed);
    }

    @PostMapping("/ptz/up")
    @Operation(
            summary = "TILT_UP",
            description = "카메라를 위쪽(TILT_UP)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto tiltUp(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.TILT_UP, speed);
    }

    @PostMapping("/ptz/down")
    @Operation(
            summary = "TILT_DOWN",
            description = "카메라를 아래쪽(TILT_DOWN)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto tiltDown(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.TILT_DOWN, speed);
    }

    @PostMapping("/ptz/up-left")
    @Operation(
            summary = "PAN_TILT_UP_LEFT",
            description = "카메라를 좌상단(PAN_TILT_UP_LEFT)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto panTiltUpLeft(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.PAN_TILT_UP_LEFT, speed);
    }

    @PostMapping("/ptz/up-right")
    @Operation(
            summary = "PAN_TILT_UP_RIGHT",
            description = "카메라를 우상단(PAN_TILT_UP_RIGHT)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto panTiltUpRight(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.PAN_TILT_UP_RIGHT, speed);
    }

    @PostMapping("/ptz/down-left")
    @Operation(
            summary = "PAN_TILT_DOWN_LEFT",
            description = "카메라를 좌하단(PAN_TILT_DOWN_LEFT)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto panTiltDownLeft(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.PAN_TILT_DOWN_LEFT, speed);
    }

    @PostMapping("/ptz/down-right")
    @Operation(
            summary = "PAN_TILT_DOWN_RIGHT",
            description = "카메라를 우하단(PAN_TILT_DOWN_RIGHT)으로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto panTiltDownRight(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.PAN_TILT_DOWN_RIGHT, speed);
    }

    @PostMapping("/ptz/zoom-in")
    @Operation(
            summary = "ZOOM_IN",
            description = "카메라를 확대(ZOOM_IN)합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto zoomIn(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.ZOOM_IN, speed);
    }

    @PostMapping("/ptz/zoom-out")
    @Operation(
            summary = "ZOOM_OUT",
            description = "카메라를 축소(ZOOM_OUT)합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto zoomOut(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password,
            @RequestParam(defaultValue = "30") int speed
    ) {
        return move(ip, port, username, password, PtzDirection.ZOOM_OUT, speed);
    }

    @PostMapping("/ptz/home")
    @Operation(
            summary = "HOME",
            description = "카메라를 홈(HOME) 위치로 이동합니다.",
            responses = {
                    @ApiResponse( responseCode = "200", description = "정상 응답",
                            content = @Content( mediaType = "application/json",
                                    schema = @Schema(implementation = PtzControlResultDto.class)
                            )
                    ),
            }
    )
    public PtzControlResultDto moveHome(
            @RequestParam String ip,
            @RequestParam(defaultValue = "80") int port,
            @RequestParam String username,
            @RequestParam String password
    ) {
        return move(ip, port, username, password, PtzDirection.HOME, 100);
    }

    // PTZ common method to handle movement
    private PtzControlResultDto move(String ip, int port, String username, String password, PtzDirection dir, int speedPercent) {
        float scaledSpeed = Math.max(1, Math.min(speedPercent, 100)) / 100.0f;// 0.01 ~ 1.0
        String result = "SUCCESS";
        String message = dir.getLabel() + " (속도: " + speedPercent + "%)";
        try {
            this.service.move(ip + ":" + port, username, password, dir, scaledSpeed);
        }
        catch (Exception e) {
            result = "FAIL";
            message = e.getMessage();
        }
        return PtzControlResultDto.builder()
                .success(result)
                .message(message)
                .build();
    }

}
