from flask import Flask, jsonify, request
import subprocess
import os
import json
import datetime
import psutil
import time

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.path.join(BASE_DIR, "config.json")
HISTORY_FILE = os.path.join(BASE_DIR, "history.json")

app = Flask(__name__)


def load_config():
    with open(CONFIG_FILE, "r", encoding="utf-8") as f:
        return json.load(f)


config = load_config()


def save_history(command, result):
    history = []

    if os.path.exists(HISTORY_FILE):
        try:
            with open(HISTORY_FILE, "r", encoding="utf-8") as f:
                history = json.load(f)
        except:
            history = []

    history.insert(0, {
        "time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "command": command,
        "result": result
    })

    history = history[:300]

    with open(HISTORY_FILE, "w", encoding="utf-8") as f:
        json.dump(history, f, ensure_ascii=False, indent=2)


def check_auth():
    api_key = request.headers.get("X-API-KEY")
    return api_key == config["server"]["api_key"]


@app.before_request
def before_request():
    if not check_auth():
        return jsonify({
            "success": False,
            "message": "Unauthorized"
        }), 401


def find_process_by_port(port):
    for proc in psutil.process_iter(["pid", "name", "cmdline"]):
        try:
            for conn in proc.net_connections(kind="inet"):
                if conn.laddr and conn.laddr.port == int(port):
                    return proc
        except:
            continue
    return None


def is_port_running(port):
    return find_process_by_port(port) is not None


def find_app(port):
    for item in config["apps"]:
        if int(item["port"]) == int(port):
            return item
    return None


def run_bat(path):
    if not os.path.exists(path):
        save_history(path, "BAT 파일 없음")
        return False, f"BAT 파일 없음: {path}"

    try:
        subprocess.Popen(
            path,
            shell=True,
            cwd=os.path.dirname(path),
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            creationflags=subprocess.CREATE_NEW_CONSOLE
        )

        save_history(path, "실행 요청 완료")

        return True, "실행 요청 완료"

    except Exception as e:
        save_history(path, str(e))
        return False, str(e)


def start_python_app(app_info):
    port = int(app_info["port"])
    path = app_info["path"]
    python_cmd = app_info.get("python", "python")

    if is_port_running(port):
        return True, f"{port}번 포트는 이미 실행 중입니다."

    if not os.path.exists(path):
        return False, f"app.py 파일 없음: {path}"

    app_dir = os.path.dirname(path)

    command = f'cd /d "{app_dir}" && {python_cmd} "{path}"'

    try:
        subprocess.Popen(
            command,
            shell=True,
            cwd=app_dir,
            creationflags=subprocess.CREATE_NEW_CONSOLE
        )

        save_history(command, f"{port}번 app.py 시작 요청")

        time.sleep(1)

        if is_port_running(port):
            return True, f"{port}번 app.py 시작 완료"
        else:
            return True, f"{port}번 app.py 시작 요청 완료. 상태 확인이 조금 늦을 수 있습니다."

    except Exception as e:
        save_history(command, str(e))
        return False, str(e)


def stop_python_app(app_info):
    port = int(app_info["port"])
    proc = find_process_by_port(port)

    if not proc:
        return True, f"{port}번 포트는 이미 정지 상태입니다."

    try:
        pid = proc.pid
        name = proc.name()

        proc.terminate()

        try:
            proc.wait(timeout=5)
        except psutil.TimeoutExpired:
            proc.kill()

        save_history(
            f"stop port {port}",
            f"PID {pid}, {name} 종료 완료"
        )

        return True, f"{port}번 포트 프로세스 종료 완료"

    except Exception as e:
        save_history(f"stop port {port}", str(e))
        return False, str(e)


@app.route("/", methods=["GET"])
def home():
    return jsonify({
        "success": True,
        "message": "Python Control Agent is running"
    })


@app.route("/status", methods=["GET"])
def status():
    current_config = load_config()

    apps_status = []

    for item in current_config["apps"]:
        port = int(item["port"])
        proc = find_process_by_port(port)

        apps_status.append({
            "name": item["name"],
            "port": port,
            "path": item["path"],
            "running": proc is not None,
            "pid": proc.pid if proc else None,
            "process_name": proc.name() if proc else None
        })

    xampp = current_config["xampp"]

    apache_port = int(xampp.get("apache_port", 8080))

    result = {
        "apps": apps_status,
        "xampp": {
            "apache_port": apache_port,
            "apache_running": is_port_running(apache_port),
            "filezilla_running": any(
                "filezilla" in (p.info["name"] or "").lower()
                for p in psutil.process_iter(["name"])
            )
        }
    }

    return jsonify({
        "success": True,
        "data": result
    })


@app.route("/apps", methods=["GET"])
def apps_list():
    current_config = load_config()

    return jsonify({
        "success": True,
        "data": current_config["apps"]
    })


@app.route("/app/<int:port>/start", methods=["POST"])
def app_start(port):
    current_config = load_config()

    target = None
    for item in current_config["apps"]:
        if int(item["port"]) == int(port):
            target = item
            break

    if not target:
        return jsonify({
            "success": False,
            "message": f"{port}번 포트 app 설정이 없습니다."
        })

    success, message = start_python_app(target)

    return jsonify({
        "success": success,
        "message": message
    })


@app.route("/app/<int:port>/stop", methods=["POST"])
def app_stop(port):
    current_config = load_config()

    target = None
    for item in current_config["apps"]:
        if int(item["port"]) == int(port):
            target = item
            break

    if not target:
        return jsonify({
            "success": False,
            "message": f"{port}번 포트 app 설정이 없습니다."
        })

    success, message = stop_python_app(target)

    return jsonify({
        "success": success,
        "message": message
    })


@app.route("/app/<int:port>/restart", methods=["POST"])
def app_restart(port):
    current_config = load_config()

    target = None
    for item in current_config["apps"]:
        if int(item["port"]) == int(port):
            target = item
            break

    if not target:
        return jsonify({
            "success": False,
            "message": f"{port}번 포트 app 설정이 없습니다."
        })

    stop_success, stop_message = stop_python_app(target)

    time.sleep(1)

    start_success, start_message = start_python_app(target)

    return jsonify({
        "success": stop_success and start_success,
        "message": f"{stop_message} / {start_message}"
    })


@app.route("/xampp/apache/start", methods=["POST"])
def apache_start():
    current_config = load_config()
    path = current_config["xampp"]["apache_start"]

    success, message = run_bat(path)

    return jsonify({
        "success": success,
        "message": message
    })


@app.route("/xampp/apache/stop", methods=["POST"])
def apache_stop():
    current_config = load_config()
    path = current_config["xampp"]["apache_stop"]

    success, message = run_bat(path)

    return jsonify({
        "success": success,
        "message": message
    })


@app.route("/xampp/filezilla/start", methods=["POST"])
def filezilla_start():
    current_config = load_config()
    path = current_config["xampp"]["filezilla_start"]

    success, message = run_bat(path)

    return jsonify({
        "success": success,
        "message": message
    })


@app.route("/xampp/filezilla/stop", methods=["POST"])
def filezilla_stop():
    current_config = load_config()
    path = current_config["xampp"]["filezilla_stop"]

    success, message = run_bat(path)

    return jsonify({
        "success": success,
        "message": message
    })


@app.route("/history", methods=["GET"])
def history():
    if not os.path.exists(HISTORY_FILE):
        return jsonify({
            "success": True,
            "data": []
        })

    with open(HISTORY_FILE, "r", encoding="utf-8") as f:
        data = json.load(f)

    return jsonify({
        "success": True,
        "data": data
    })


if __name__ == "__main__":
    host = config["server"].get("host", "0.0.0.0")
    port = int(config["server"].get("agent_port", 9000))

    app.run(host=host, port=port)