#!/usr/bin/env python3
# 1서버 TTS 워커 — 페이지 번호 받아 WAV 생성 후 3서버 FTP 업로드
import sys, os, json, time, ftplib, urllib.request

# 모델 캐시 경로 강제 (www-data 권한으로 실행 시 /root/.cache 접근 불가 회피)
os.environ['HF_HOME'] = '/var/www/tts_worker/.cache/huggingface'
os.environ['XDG_CACHE_HOME'] = '/var/www/tts_worker/.cache'
os.environ['HOME'] = '/var/www/tts_worker'

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from supertonic import TTS
from tts_preprocessor import preprocess_for_tts

LECTURE_URL = "https://land114.net/lecture_data.json"
OUTPUT_DIR  = "/var/www/tts_worker/output"
LOG_DIR     = "/var/www/tts_worker/logs"
STATUS_DIR  = "/var/www/tts_worker/status"


def write_status(page_num, stage, percent, msg=""):
    path = os.path.join(STATUS_DIR, f"page_{page_num:02d}.json")
    data = {
        "page": page_num,
        "stage": stage,
        "percent": percent,
        "msg": msg,
        "ts": time.time(),
    }
    try:
        with open(path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False)
    except Exception:
        pass

FTP_HOST = "ji114.mycafe24.com"
FTP_USER = "ji114"
FTP_PASS = "Ee7782100"


def log(msg):
    line = f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] {msg}"
    print(line, flush=True)
    with open(os.path.join(LOG_DIR, "worker.log"), "a", encoding="utf-8") as f:
        f.write(line + "\n")


def fetch_lecture_data():
    raw = urllib.request.urlopen(f"{LECTURE_URL}?v={int(time.time())}").read()
    if raw[:3] == b'\xef\xbb\xbf':
        raw = raw[3:]
    return json.loads(raw.decode('utf-8'))


def generate_one(page_num, desc, tts, style):
    # desc_voice (이미 한글화된 사람 스크립트) — preprocessor 우회
    # desc (원본) — preprocessor 통과
    is_voice_script = any(ch in desc for ch in ['엠투', '내지'])
    if not is_voice_script:
        desc = preprocess_for_tts(desc.replace('\n', ' '))
    # 800자 제한 제거 — Supertonic max_chunk_length로 긴 텍스트도 자동 청크 처리
    output_path = os.path.join(OUTPUT_DIR, f"page_{page_num:02d}_desc.wav")
    wav, dur = tts.synthesize(
        text=desc,
        lang="ko",
        voice_style=style,
        total_steps=8,
        speed=1.0,
        max_chunk_length=300,  # 청크당 300자, 자동 분할 후 이어붙임
        silence_duration=0.3,  # 청크 간 0.3초 무음
    )
    tts.save_audio(wav, output_path)
    return output_path, dur[0]


def ftp_upload(local_path, remote_path):
    with ftplib.FTP(FTP_HOST, FTP_USER, FTP_PASS, timeout=30) as ftp:
        with open(local_path, 'rb') as f:
            ftp.storbinary(f"STOR {remote_path}", f)


def process_pages(pages):
    log(f"START pages={pages}")
    for pn in pages:
        write_status(pn, "queued", 5, "대기 중")

    write_status(pages[0], "loading", 10, "TTS 엔진 로딩 중")
    # 1서버 6코어 활용 — intra=합성 내부 병렬, inter=연산 간 병렬
    tts = TTS(auto_download=True, intra_op_num_threads=6, inter_op_num_threads=2)
    style = tts.get_voice_style(voice_name="F4")
    data = fetch_lecture_data()
    all_pages = data['pages']

    success, failed = [], []
    for pn in pages:
        pn_str = str(pn)
        if pn_str not in all_pages:
            failed.append((pn, "no data"))
            write_status(pn, "failed", 100, "데이터 없음")
            continue
        # desc_voice 우선 사용 (사람이 다듬은 음성 스크립트)
        # 없으면 desc 사용 (자동 변환만)
        desc_voice = all_pages[pn_str].get('desc_voice', '').strip()
        desc = desc_voice if desc_voice else all_pages[pn_str].get('desc', '')
        if not desc:
            failed.append((pn, "empty desc"))
            write_status(pn, "failed", 100, "본문 비어있음")
            continue
        try:
            stage_msg = "음성 합성 중 (사람 스크립트)" if desc_voice else "음성 합성 중 (자동 변환)"
            write_status(pn, "synth", 30, stage_msg)
            wav_path, dur = generate_one(pn, desc, tts, style)
            write_status(pn, "upload", 90, "3서버 업로드 중")
            ftp_upload(wav_path, f"tts_samples/pages/page_{pn:02d}_desc.wav")
            log(f"OK page={pn} dur={dur:.1f}s")
            write_status(pn, "done", 100, f"완료 ({dur:.1f}초 길이)")
            success.append(pn)
        except Exception as e:
            log(f"FAIL page={pn} err={e}")
            write_status(pn, "failed", 100, str(e)[:80])
            failed.append((pn, str(e)[:80]))

    log(f"DONE success={len(success)} failed={len(failed)}")
    return {"success": success, "failed": failed}


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(json.dumps({"error": "usage: tts_worker_server.py <page_num|comma_list>"}))
        sys.exit(1)
    arg = sys.argv[1]
    if ',' in arg:
        pages = [int(x) for x in arg.split(',')]
    else:
        pages = [int(arg)]
    result = process_pages(pages)
    print(json.dumps(result, ensure_ascii=False))
