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

import com.its.onvif.cmra.server.dto.PtzVector;
import com.its.onvif.cmra.server.dto.request.PtzControlRequestDto;
import com.its.onvif.cmra.server.dto.response.CameraDeviceInfoDto;
import com.its.onvif.cmra.server.dto.response.CameraStreamProfileDto;
import com.its.onvif.cmra.server.dto.PtzDirection;
import com.its.onvif.cmra.server.dto.response.PtzStatusDto;
import de.onvif.soap.OnvifDevice;
import de.onvif.soap.devices.InitialDevices;
import de.onvif.soap.devices.PtzDevices;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.onvif.ver10.device.wsdl.GetDeviceInformationResponse;
import org.onvif.ver10.schema.*;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Slf4j
@Getter
@Service
@RequiredArgsConstructor
public class OnvifCameraService {

    @PostConstruct
    private void init() {
        log.info("CmraOnvifServerService.init: start.");
        log.info("CmraOnvifServerService.init: ..end.");
    }

    private String injectCredentials(String uri, String username, String password) {
        if (uri == null || uri.isEmpty() || username == null || password == null) {
            return uri;
        }

        try {
            URI parsed = new URI(uri);
            String userInfo = username + ":" + password;

            URI rebuilt = new URI(
                    parsed.getScheme(),
                    userInfo,
                    parsed.getHost(),
                    parsed.getPort(),
                    parsed.getPath(),
                    parsed.getQuery(),
                    parsed.getFragment()
            );

            return rebuilt.toString();
        } catch (URISyntaxException e) {
            log.warn("URI 재구성 실패: {}", uri, e);
            return uri; // 실패 시 원본 반환
        }
    }
    private List<CameraStreamProfileDto> getStreams(OnvifDevice device) {
        InitialDevices initialDevices = device.getDevices();
        List<CameraStreamProfileDto> result = new ArrayList<>();
        List<Profile> profiles = initialDevices.getProfiles();
        if (profiles.isEmpty()) {
            return result; // 프로파일이 없으면 빈 리스트 반환
        }

        for (Profile profile : profiles) {
            String streamUri = profile.getVideoEncoderConfiguration().getName();
            try {
                streamUri = device.getMedia().getRTSPStreamUri(profile.getToken());
//                streamUri = injectCredentials(streamUri, device.getUsername(), device.getEncryptedPassword());
//                streamUri = injectCredentials(streamUri, device.getUsername(), device.getEncryptedPassword());
            }
            catch (Exception e) {
                log.warn("RTSP URI 추출 실패 - profile: {}", profile.getName(), e);
            }
            result.add(CameraStreamProfileDto.builder()
                    .profileName(profile.getName())
                    .streamUri(streamUri)
                    .encoding(profile.getVideoEncoderConfiguration().getEncoding().value())
                    .width(profile.getVideoEncoderConfiguration().getResolution().getWidth())
                    .height(profile.getVideoEncoderConfiguration().getResolution().getHeight())
                    .bitrate(profile.getVideoEncoderConfiguration().getRateControl().getBitrateLimit())
                    .frameRate(profile.getVideoEncoderConfiguration().getRateControl().getFrameRateLimit())
                    .build());
        }
        return result;
    }

    private String getToken(InitialDevices initialDevices) {
        List<Profile> profiles = initialDevices.getProfiles();
        if (profiles.isEmpty()) {
            throw new IllegalStateException("PTZ 제어용 프로파일이 존재하지 않습니다.");
        }
        return profiles.get(0).getToken(); // 첫 번째 프로파일의 토큰 반환
    }

    public CameraDeviceInfoDto getDeviceInfo(String host, String username, String password) throws Exception {
        OnvifDevice device = new OnvifDevice(host, username, password);

        InitialDevices initialDevices = device.getDevices();
        GetDeviceInformationResponse response = initialDevices.getDeviceInformation();

        String manufacturer = response.getManufacturer();
        String model = response.getModel();
        String firmwareVersion = response.getFirmwareVersion();

        // RTSP 스트림 URI 도 함께 반환
        //String streamUri = initialDevices.getProfiles().get(0).getVideoEncoderConfiguration().getName();
        return CameraDeviceInfoDto.builder()
                .manufacturer(manufacturer)
                .model(model)
                .firmwareVersion(firmwareVersion)
                .hostname(device.getHostname())
                .streams(getStreams(device))
                .build();
    }

    public List<CameraStreamProfileDto> getStreamProfiles(String host, String username, String password) throws Exception {
        OnvifDevice device = new OnvifDevice(host, username, password);
        return getStreams(device);
    }

    public void move(String host, String username, String password, PtzDirection dir, float speed) throws Exception {
        OnvifDevice device = new OnvifDevice(host, username, password);
        String token = getToken(device.getDevices());
        PtzDevices ptz = device.getPtz();

        PtzVector v = dir.getVector();
        ptz.relativeMove(token, v.getPan() * speed, v.getTilt() * speed, v.getZoom() * speed);
    }

    public void stop(String host, String username, String password) throws Exception {
        OnvifDevice device = new OnvifDevice(host, username, password);
        String token = getToken(device.getDevices());
        device.getPtz().stopMove(token);
    }
    public void move(PtzControlRequestDto req, PtzDirection dir) throws Exception {
        OnvifDevice device = new OnvifDevice(req.getIp(), req.getUsername(), req.getPassword());
        String token = getToken(device.getDevices());
        PtzDevices ptz = device.getPtz();

        float scaledSpeed = Math.max(1, Math.min(req.getSpeed(), 100)) / 100.0f;// 0.01 ~ 1.0
        PtzVector v = dir.getVector();
        ptz.relativeMove(token, v.getPan() * scaledSpeed, v.getTilt() * scaledSpeed, v.getZoom() * scaledSpeed);
    }

    public void stop(PtzControlRequestDto req) throws Exception {
        OnvifDevice device = new OnvifDevice(req.getIp(), req.getUsername(), req.getPassword());
        String token = getToken(device.getDevices());
        device.getPtz().stopMove(token);
    }

    public PtzStatusDto getStatus(String host, String username, String password) throws Exception {
        OnvifDevice device = new OnvifDevice(host, username, password);
        String token = getToken(device.getDevices());
        PTZStatus status = device.getPtz().getStatus(token);
        PTZVector pos = status.getPosition();

        return PtzStatusDto.builder()
                .pan(pos.getPanTilt().getX())
                .tilt(pos.getPanTilt().getY())
                .zoom(pos.getZoom().getX())
                .build();
    }

}
