package org.ssafy.pasila.domain.live.service;

import io.openvidu.java.client.*;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.ssafy.pasila.domain.apihandler.ErrorCode;
import org.ssafy.pasila.domain.live.entity.Live;
import org.ssafy.pasila.domain.live.repository.LiveRepository;
import org.ssafy.pasila.domain.live.utils.RetryException;
import org.ssafy.pasila.domain.live.utils.RetryOptions;
import org.ssafy.pasila.global.util.JwtUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class OpenviduService {

    @Value("${openvidu.openvidu_url}")
    public String OPENVIDU_URL;

    @Value("${openvidu.openvidu_secret}")
    public String OPENVIDU_SECRET;

    OpenVidu openvidu;

    private final JwtUtil jwtUtil;

    private final LiveRepository liveRepository;

    @PostConstruct
    public void init() {
        this.openvidu = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET);
    }

    public Session createSession(Map<String, Object> params)
            throws OpenViduJavaClientException, OpenViduHttpException, InterruptedException, RetryException {
        return createSession(params, new RetryOptions());
    }

    private Session createSession(Map<String, Object> params, RetryOptions retryOptions)
            throws OpenViduJavaClientException, OpenViduHttpException, InterruptedException, RetryException {

        while (retryOptions.canRetry()) {
            try {
                SessionProperties properties = SessionProperties.fromJson(params).build();
                Session session = openvidu.createSession(properties);
                session.fetch();
                return session;
            } catch (OpenViduHttpException e) {
                if (e.getStatus() == 404 || (e.getStatus() >= 500 && e.getStatus() <= 504)) {
                    // 404: session does not exist
                    // 502 ~ 504: OpenVidu Server is not available
                    retryOptions.retrySleep();
                } else {
                    throw e;
                }
            }
        }
        throw new RetryException(ErrorCode.MAX_RETRIES_EXCEEDED);
    }

    public String createConnection(String sessionId, String token)
            throws OpenViduHttpException, InterruptedException, OpenViduJavaClientException {
        Map<String, Object> params = new HashMap<>();
        if(token != null && !token.isEmpty() && isShowHost(sessionId, getMemberIdFromToken(token))){
            params.put("role", "PUBLISHER");
        }else {
            params.put("role", "SUBSCRIBER");
        }
        Session session = openvidu.getActiveSession(sessionId);
        return createConnection(session, params, new RetryOptions()).getToken();
    }

    private Connection createConnection(Session session, Map<String, Object> params, RetryOptions retryOptions)
            throws OpenViduJavaClientException, OpenViduHttpException, InterruptedException {
        ConnectionProperties properties = ConnectionProperties.fromJson(params).build();
        Connection connection = null;

        while (retryOptions.canRetry()) {
            try {
                connection = session.createConnection(properties);
                break;
            } catch (OpenViduHttpException e) {
                if (e.getStatus() >= 500 && e.getStatus() <= 504) {
                    retryOptions.retrySleep();
                } else {
                    throw e;
                }
            }
        }

        if (connection == null) {
            throw new RetryException(ErrorCode.MAX_RETRIES_EXCEEDED);
        }

        return connection;
    }

    private String extractFromToken(String token) {
        String[] parts = token.split(" ");
        if (parts.length == 2 && parts[0].equals("Bearer")) {
            return parts[1];
        } else {
            throw new IllegalArgumentException("Invalid token");
        }
    }

    private Long getMemberIdFromToken(String token) {
        return jwtUtil.getUserId(extractFromToken(token));
    }

    private boolean isShowHost(String sessionId, Long memberId){
        Live live = liveRepository.findById(sessionId)
                .orElseThrow(() -> new IllegalArgumentException("해당 아이디에 대한 라이브 정보가 없습니다."));
        return live.getMember().getId().equals(memberId);
    }

    /**
     * RECORDING
     **/

    // 녹화 시작
    public Recording startRecording(String sessionId) throws OpenViduJavaClientException, OpenViduHttpException {
        return openvidu.startRecording(sessionId);
    }

    // 녹화 종료
    public Recording stopRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException {
        return openvidu.stopRecording(recordingId);
    }

    // 녹화 삭제
    public void deleteRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException {
        List<Recording> recordings = this.openvidu.listRecordings();
        for(Recording recording : recordings){
            if(recording.getId().equals(recordingId)) {
                openvidu.deleteRecording(recordingId);
            }
        }
    }
}