package com.kt.distribution.client;

import com.kt.distribution.dto.DistributionRequest;
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.LoadState;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * Naver Blog Client using Playwright
 * 네이버 블로그 포스팅 자동화 클라이언트
 *
 * @author Backend Developer
 * @since 2025-10-29
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "naver.blog.enabled", havingValue = "true", matchIfMissing = false)
public class NaverBlogClient {

    @Value("${naver.blog.username:}")
    private String username;

    @Value("${naver.blog.password:}")
    private String password;

    @Value("${naver.blog.blog-id:}")
    private String blogId;

    @Value("${naver.blog.headless:false}")
    private boolean headless;

    @Value("${naver.blog.session-path:playwright-sessions}")
    private String sessionPath;

    private Playwright playwright;
    private Browser browser;
    private BrowserContext context;

    /**
     * Playwright 초기화
     */
    @PostConstruct
    public void init() {
        try {
            log.info("Initializing Playwright for Naver Blog");
            playwright = Playwright.create();

            browser = playwright.chromium().launch(new BrowserType.LaunchOptions()
                    .setHeadless(headless)
                    .setSlowMo(100)); // 안정성을 위한 느린 실행

            // 세션 디렉토리 생성
            File sessionDir = new File(sessionPath);
            if (!sessionDir.exists()) {
                sessionDir.mkdirs();
                log.info("Created session directory: {}", sessionPath);
            }

            // 세션 파일 경로
            Path sessionFilePath = Paths.get(sessionPath, "naver-blog-session.json");

            // 세션 파일이 있으면 로드, 없으면 새로운 컨텍스트 생성
            if (Files.exists(sessionFilePath)) {
                log.info("Loading existing session from: {}", sessionFilePath);
                context = browser.newContext(new Browser.NewContextOptions()
                        .setStorageStatePath(sessionFilePath));
            } else {
                log.info("No existing session found, creating new context");
                context = browser.newContext();
            }

            log.info("Playwright initialized successfully");
        } catch (Exception e) {
            log.error("Failed to initialize Playwright", e);
            throw new RuntimeException("Playwright initialization failed", e);
        }
    }

    /**
     * 네이버 블로그에 포스팅
     *
     * @param request DistributionRequest
     * @return 포스팅 URL
     * @throws Exception 포스팅 실패 시
     */
    public String postToBlog(DistributionRequest request) throws Exception {
        Page page = null;
        try {
            page = context.newPage();
            // 타임아웃을 5분(300000ms)으로 설정
            page.setDefaultTimeout(300000);

            // 로그인 확인 및 처리
            if (!isLoggedIn(page)) {
                login(page);
            }

            // 블로그 글쓰기 페이지로 이동
            String writeUrl = String.format("https://blog.naver.com/%s/postwrite", blogId);
            page.navigate(writeUrl);
            page.waitForLoadState(LoadState.NETWORKIDLE);


            // 도움말 팝업이 있으면 닫기
            try {
                page.waitForTimeout(5000); // 충분히 대기 필요

                Locator helpPanel = page.locator("[class*='help-panel']");

                if (helpPanel.isVisible(new Locator.IsVisibleOptions().setTimeout(2000))) {
                    log.debug("Help dialog detected, closing it");

                    // 팝업 안의 닫기 버튼 찾기
                    Locator closeBtn = page.locator("button[class*='se-help-panel-close-button']");
                    closeBtn.click();
                    Thread.sleep(500);
                    log.debug("Help dialog closed");
                } else{
                    log.debug("--------------------- 도움말 없음");
                }
            } catch (Exception e) {
                log.debug("No help dialog found or already closed");
            }

            // 제목 입력
            Locator titleInput = page.locator(".se-text-paragraph").first();
            titleInput.click();
            titleInput.pressSequentially(request.getTitle(), new Locator.PressSequentiallyOptions().setDelay(50));
            log.debug("Title entered: {}", request.getTitle());

            // 본문 입력
            Locator editorInput = page.locator(".se-text-paragraph").nth(1);
            editorInput.click();
            titleInput.pressSequentially(request.getDescription(), new Locator.PressSequentiallyOptions().setDelay(50));
            log.debug("Content entered");

            // 이미지가 있으면 업로드
            if (request.getImageUrl() != null && !request.getImageUrl().isEmpty()) {
                uploadImage(page, request.getImageUrl());
            }

            // 발행 버튼 클릭
            page.locator("button[class*='publish_btn']").click();
            page.waitForLoadState(LoadState.NETWORKIDLE);
            page.locator("button[class*='confirm_btn']").click();
            page.waitForLoadState(LoadState.NETWORKIDLE);

            page.waitForTimeout(5000); // 충분히 대기 필요

            // 포스팅 URL 가져오기
            String postUrl = page.url();
            log.info("Post published successfully: {}", postUrl);

            return postUrl;

        } catch (Exception e) {
            log.error("Failed to post to Naver blog: eventId={}, error={}",
                    request.getEventId(), e.getMessage(), e);
            throw e;
        } finally {
            if (page != null) {
                page.close();
            }
        }
    }

    /**
     * 로그인 상태 확인
     *
     * @param page Page
     * @return 로그인 여부
     */
    private boolean isLoggedIn(Page page) {
        try {
            page.navigate("https://blog.naver.com");
            page.waitForLoadState(LoadState.NETWORKIDLE);

            // 로그인 버튼이 보이지 않으면 로그인된 상태
            // ID 기반 선택자 사용으로 strict mode violation 방지
            return !page.locator("#gnb_login_button").isVisible();
        } catch (Exception e) {
            log.warn("Failed to check login status", e);
            return false;
        }
    }

    /**
     * 네이버 로그인 (수동 로그인 대기 방식)
     *
     * @param page Page
     * @throws Exception 로그인 실패 시
     */
    private void login(Page page) throws Exception {
        try {
            log.info("Starting Naver manual login process");
            log.info("=================================================");
            log.info("Please login manually in the browser window");
            log.info("브라우저 창에서 수동으로 로그인해주세요");
            log.info("=================================================");

            // 네이버 로그인 페이지로 이동
            page.navigate("https://nid.naver.com/nidlogin.login");
            page.waitForLoadState(LoadState.NETWORKIDLE);

            // 사용자가 수동으로 로그인할 때까지 대기 (URL이 변경될 때까지)
            // 로그인 성공 시 URL이 nid.naver.com에서 벗어남
            log.info("Waiting for manual login... (Timeout: 30 seconds)");

            try {
                // 30초 동안 URL이 nid.naver.com을 벗어날 때까지 대기
                page.waitForURL(url -> !url.contains("nid.naver.com"),
                    new Page.WaitForURLOptions().setTimeout(30000));

                log.info("Login URL changed, assuming login successful");

            } catch (Exception e) {
                log.error("Login timeout or failed", e);
                throw new Exception("Manual login timeout or failed after 30 seconds");
            }

            // 추가 안정화 대기
            page.waitForLoadState(LoadState.NETWORKIDLE);
            Thread.sleep(2000); // 2초 추가 대기

            // 세션 저장
            context.storageState(new BrowserContext.StorageStateOptions()
                    .setPath(Paths.get(sessionPath, "naver-blog-session.json")));

            log.info("Naver manual login successful, session saved");
            log.info("Current URL: {}", page.url());

        } catch (Exception e) {
            log.error("Naver manual login process failed", e);
            throw new Exception("Naver manual login failed: " + e.getMessage(), e);
        }
    }

    /**
     * 이미지 업로드
     *
     * @param page Page
     * @param imageUrl 이미지 URL
     */
    private void uploadImage(Page page, String imageUrl) {
        try {
            log.debug("Uploading image: {}", imageUrl);

            // 이미지 업로드 버튼 클릭
            page.locator("button[aria-label='사진']").click();

            // URL로 이미지 추가 (실제 구현은 네이버 블로그 UI에 따라 조정 필요)
            // 여기서는 간단히 로그만 남김
            log.info("Image upload placeholder - URL: {}", imageUrl);

        } catch (Exception e) {
            log.warn("Failed to upload image: {}", e.getMessage());
        }
    }

    /**
     * Playwright 리소스 정리
     */
    @PreDestroy
    public void cleanup() {
        try {
            if (context != null) {
                context.close();
            }
            if (browser != null) {
                browser.close();
            }
            if (playwright != null) {
                playwright.close();
            }
            log.info("Playwright resources cleaned up");
        } catch (Exception e) {
            log.error("Failed to cleanup Playwright resources", e);
        }
    }

    /**
     * 수동으로 브라우저 컨텍스트 새로고침
     * 장시간 사용 시 세션 만료 방지용
     */
    public void refreshContext() {
        try {
            if (context != null) {
                context.close();
            }

            // 세션 파일 경로
            Path sessionFilePath = Paths.get(sessionPath, "naver-blog-session.json");

            // 세션 파일이 있으면 로드, 없으면 새로운 컨텍스트 생성
            if (Files.exists(sessionFilePath)) {
                log.info("Refreshing context with existing session");
                context = browser.newContext(new Browser.NewContextOptions()
                        .setStorageStatePath(sessionFilePath));
            } else {
                log.info("Refreshing context without session");
                context = browser.newContext();
            }

            log.info("Browser context refreshed");
        } catch (Exception e) {
            log.error("Failed to refresh context", e);
        }
    }
}
