# 경로/파일: /opt/realcom-nas/realcom-nas-ui/modules/samba_conf.py
# 설명:
# - /etc/samba/nas_smb.conf 의 [STORAGE] 섹션의 "valid users"를 자동 갱신
# - testparm 체크 후 Samba 재시작(smbd + (가능하면) nmbd)
# - Flask에서 호출해도 앱이 죽지 않게 예외를 내부에서 흡수하고 (ok, msg)로 반환

from __future__ import annotations

import os
import re
import subprocess
from typing import List, Tuple, Optional

# =========================
# Ctrl+F: SAMBA_CONF_PATH
# =========================
# ✅ 플레이북과 동일 기본값. 필요하면 systemd Environment=REALNAS_SAMBA_CONF=... 로 override 가능
SAMBA_CONF_PATH = os.environ.get("REALNAS_SAMBA_CONF", "/etc/samba/nas_smb.conf")

# ✅ 항상 포함할 유저들(기본: admin + realnas)
#    필요하면 "admin realnas" 같은 문자열로 override 가능
REQUIRED_USERS = os.environ.get("REALNAS_REQUIRED_SAMBA_USERS", "admin realnas").split()


def _run(cmd: List[str], input_text: Optional[str] = None) -> subprocess.CompletedProcess:
    """내부용 subprocess wrapper (절대 예외 throw 안 함)"""
    try:
        return subprocess.run(
            cmd,
            input=input_text,
            text=True,
            capture_output=True,
            check=False,
        )
    except Exception as e:
        return subprocess.CompletedProcess(cmd, returncode=1, stdout="", stderr=str(e))


def _sudo(cmd: List[str], input_text: Optional[str] = None) -> subprocess.CompletedProcess:
    """
    sudo -n(비번 프롬프트 금지)로 실행.
    비번 요구/권한 없음이면 바로 실패(returncode!=0) → Flask 안 멈춤.
    """
    return _run(["sudo", "-n"] + cmd, input_text=input_text)


def _out(p: subprocess.CompletedProcess) -> str:
    return ((p.stdout or "") + (("\n" + p.stderr) if p.stderr else "")).strip()


def testparm_check(conf_path: str = SAMBA_CONF_PATH) -> Tuple[bool, str]:
    """Samba 설정 문법 검사 (대상 파일 지정)"""
    p = _run(["testparm", "-s", conf_path])
    return (p.returncode == 0), _out(p)


def _restart_unit(unit: str) -> Tuple[bool, str]:
    """systemctl restart <unit>"""
    p = _sudo(["systemctl", "restart", unit])
    return (p.returncode == 0), _out(p)


def restart_samba() -> Tuple[bool, str]:
    """
    Samba 재시작
    - smbd는 필수로 성공해야 함
    - nmbd는 환경에 따라 없거나(mask) 비활성일 수 있으니 실패해도 전체 실패로 치지 않음
    """
    ok_smbd, out_smbd = _restart_unit("smbd")
    ok_nmbd, out_nmbd = _restart_unit("nmbd")

    msg_parts = []
    msg_parts.append(f"[smbd] {'OK' if ok_smbd else 'FAIL'}")
    if out_smbd:
        msg_parts.append(out_smbd)

    msg_parts.append(f"[nmbd] {'OK' if ok_nmbd else 'SKIP/FAIL'}")
    if out_nmbd:
        msg_parts.append(out_nmbd)

    return ok_smbd, "\n".join(msg_parts).strip()


def _parse_storage_block(conf_text: str):
    """[STORAGE] 섹션 블록을 찾아서 (start, end, body_text) 반환"""
    m = re.search(r"(?ms)^\[STORAGE\]\s*$([\s\S]*?)(?=^\[|\Z)", conf_text)
    if not m:
        return None
    return m.start(), m.end(), m.group(1)


def update_storage_valid_users(users: List[str]) -> Tuple[bool, str]:
    """
    [STORAGE] 섹션의 valid users 를 users 리스트 기반으로 갱신.
    - REQUIRED_USERS(기본 admin+realnas) 항상 포함
    - 중복 제거 + 정렬
    - sudo 권한이 없으면 "권한 없음"으로 실패 처리하되 앱은 안 죽음
    """
    try:
        # 1) users 정리
        cleaned: List[str] = []
        for u in users or []:
            u = (u or "").strip()
            if u:
                cleaned.append(u)

        # ✅ 필수 유저 강제 포함
        for ru in REQUIRED_USERS:
            ru = (ru or "").strip()
            if ru and ru not in cleaned:
                cleaned.append(ru)

        cleaned = sorted(set(cleaned))

        if not os.path.exists(SAMBA_CONF_PATH):
            return False, f"conf not found: {SAMBA_CONF_PATH}"

        # 2) 파일 읽기(읽기는 보통 가능)
        try:
            with open(SAMBA_CONF_PATH, "r", encoding="utf-8") as f:
                content = f.read()
        except Exception as e:
            return False, f"read failed: {e}"

        parsed = _parse_storage_block(content)
        if not parsed:
            return False, "[STORAGE] section not found"

        start, end, body = parsed

        # 3) valid users 라인 만들기 (기존 들여쓰기 4칸 유지)
        new_line = f"    valid users = {' '.join(cleaned)}"

        if re.search(r"(?m)^\s*valid users\s*=", body):
            body2 = re.sub(r"(?m)^\s*valid users\s*=.*$", new_line, body)
        else:
            body2 = body.rstrip() + "\n" + new_line + "\n"

        new_block = "[STORAGE]\n" + body2.lstrip("\n")
        new_content = content[:start] + new_block + content[end:]

        # 4) 내용이 바뀌었으면 쓰기
        if new_content != content:
            # 백업(권한 없으면 실패하지만 앱은 안 죽음)
            _sudo(["cp", "-a", SAMBA_CONF_PATH, SAMBA_CONF_PATH + ".bak"])

            # sudo tee로 덮어쓰기 (sudoers에 tee/cp 권한 없으면 여기서 실패함)
            p = _sudo(["tee", SAMBA_CONF_PATH], input_text=new_content)
            if p.returncode != 0:
                return False, (
                    "write failed (sudo permission needed)\n"
                    f"- target: {SAMBA_CONF_PATH}\n"
                    f"- stderr: {(_out(p) or 'unknown')}"
                )

        # 5) testparm 체크 (우리가 수정한 conf 파일 대상으로)
        ok, out = testparm_check(SAMBA_CONF_PATH)
        if not ok:
            return False, f"testparm failed:\n{out}"

        # 6) Samba 재시작
        ok2, out2 = restart_samba()
        if not ok2:
            return False, f"samba restart failed:\n{out2}"

        return True, f"ok\n{out2}".strip()

    except Exception as e:
        return False, f"exception: {e}"


# =========================
# CLI 테스트용 (Flask import 시엔 아무 일도 안 함)
# =========================
if __name__ == "__main__":
    # 선택: 외부에서 users를 넣고 싶으면 환경변수로
    # 예) REALNAS_TEST_USERS="admin realnas user1" python samba_conf.py
    test_users = os.environ.get("REALNAS_TEST_USERS", "").split()
    ok, msg = update_storage_valid_users(test_users)
    print(("OK" if ok else "FAIL") + "\n" + msg)

