package com.arms.api.util;

import com.arms.config.GiteaUserConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

@Component
@RequiredArgsConstructor
@Slf4j
public class GiteaHttpURLConnection {

    private final GiteaUserConfig giteaUserConfig;

    // --- 저수준 메서드 (Low-level) ---

    /**
     * Basic Auth 인증이 설정된 HttpURLConnection을 생성합니다.
     *
     * @param url    요청 URL
     * @param method HTTP 메서드 (GET, POST, PUT, DELETE)
     * @return 인증 헤더가 설정된 HttpURLConnection
     */
    @SuppressWarnings("java:S2647")
    public HttpURLConnection createConnection(String url, String method) throws Exception {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();

        String auth = giteaUserConfig.getGitUsername() + ":" + giteaUserConfig.getGitPassword();
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));

        connection.setRequestMethod(method);
        connection.setRequestProperty("Authorization", "Basic " + encodedAuth);

        if ("POST".equals(method) || "PUT".equals(method) || "DELETE".equals(method)) {
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json");
        }

        return connection;
    }

    /**
     * GET 요청을 위한 인증된 연결을 생성합니다.
     *
     * @param url 요청 URL
     * @return 인증 헤더가 설정된 HttpURLConnection (GET)
     */
    public HttpURLConnection createGetConnection(String url) throws Exception {
        return createConnection(url, "GET");
    }

    /**
     * 연결의 응답 본문을 문자열로 읽습니다.
     *
     * @param connection 응답을 읽을 HttpURLConnection
     * @return 응답 본문 문자열
     */
    public String readResponseBody(HttpURLConnection connection) throws Exception {
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        }
    }

    /**
     * 연결에 요청 본문을 씁니다.
     *
     * @param connection 요청 본문을 쓸 HttpURLConnection
     * @param body       요청 본문 문자열
     */
    public void writeRequestBody(HttpURLConnection connection, String body) throws Exception {
        try (OutputStream os = connection.getOutputStream()) {
            os.write(body.getBytes(StandardCharsets.UTF_8));
            os.flush();
        }
    }

    // --- 고수준 메서드 (파일 CRUD) ---

    /**
     * Gitea 저장소의 파일을 업데이트합니다. (PUT)
     *
     * @param apiUrl   Gitea API 디렉토리 URL
     * @param fileName 업데이트할 파일명
     * @param sha      기존 파일의 SHA 값
     * @param branch   대상 브랜치
     * @param contents 파일 내용 (평문 - 내부에서 Base64 인코딩)
     * @return HTTP 응답 코드
     */
    public int updateFile(String apiUrl,
                        String fileName,
                        String sha,
                        String branch,
                        String contents) throws Exception {
        HttpURLConnection connection = createConnection(apiUrl + fileName, "PUT");

        String content = Base64.getEncoder().encodeToString(contents.getBytes(StandardCharsets.UTF_8));
        String requestBody = String.format(
                "{\"content\":\"%s\",\"message\":\"Update %s\",\"sha\":\"%s\",\"branch\":\"%s\"}",
                content, fileName, sha, branch
        );

        writeRequestBody(connection, requestBody);

        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            log.info("File {} updated successfully.", fileName);
        } else {
            log.error("Failed to update file {}. Response code: {}", fileName, responseCode);
        }
        return responseCode;
    }

    /**
     * Gitea 저장소에 새 파일을 생성합니다. (POST)
     *
     * @param apiUrl   Gitea API 디렉토리 URL
     * @param fileName 생성할 파일명
     * @param branch   대상 브랜치
     * @param contents 파일 내용 (평문 - 내부에서 Base64 인코딩)
     * @return HTTP 응답 코드
     */
    public int createFile(String apiUrl,
                        String fileName,
                        String branch,
                        String contents) throws Exception {
        HttpURLConnection connection = createConnection(apiUrl + fileName, "POST");

        String content = Base64.getEncoder().encodeToString(contents.getBytes(StandardCharsets.UTF_8));
        String requestBody = String.format(
                "{\"content\":\"%s\",\"message\":\"Create %s\",\"branch\":\"%s\"}",
                content, fileName, branch
        );

        writeRequestBody(connection, requestBody);

        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_CREATED) {
            log.info("File {} created successfully.", fileName);
        } else {
            log.error("Failed to create file {}. Response code: {}", fileName, responseCode);
        }
        return responseCode;
    }

    /**
     * Gitea 저장소의 파일을 삭제합니다. (DELETE)
     *
     * @param apiUrl   Gitea API 디렉토리 URL
     * @param fileName 삭제할 파일명
     * @param sha      기존 파일의 SHA 값
     * @param branch   대상 브랜치
     * @return HTTP 응답 코드
     */
    public int deleteFile(String apiUrl,
                        String fileName,
                        String sha,
                        String branch) throws Exception {
        HttpURLConnection connection = createConnection(apiUrl + fileName, "DELETE");

        String requestBody = String.format(
                "{\"message\":\"Delete %s\",\"sha\":\"%s\",\"branch\":\"%s\"}",
                fileName, sha, branch
        );

        writeRequestBody(connection, requestBody);

        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            log.info("File {} deleted successfully.", fileName);
        } else {
            log.error("Failed to delete file {}. Response code: {}", fileName, responseCode);
        }
        return responseCode;
    }
}
