#!/usr/bin/env python3
# Перенос всех фото в «Фото домов»: move (серверно), конфликты имён -> суффикс
import json, os, time, urllib.request, urllib.parse
from concurrent.futures import ThreadPoolExecutor, as_completed

tok = None
for line in open("/root/site/yandex/.env_hram", encoding="utf-8"):
    if line.startswith("YANDEX2_ACCESS_TOKEN="):
        tok = line.split("=", 1)[1].strip()
H = {"Authorization": f"OAuth {tok}"}

def api_move(src, dst):
    url = "https://cloud-api.yandex.net/v1/disk/resources/move?" + urllib.parse.urlencode({
        "from": src, "path": dst, "overwrite": "false"})
    req = urllib.request.Request(url, method="POST", headers=H)
    try:
        with urllib.request.urlopen(req, timeout=120) as r:
            return r.status
    except urllib.error.HTTPError as e:
        body = e.read().decode(errors="replace")
        if "already exists" in body or "ConflictError" in body or e.code == 409:
            return 409
        return e.code

def disk_mkdir(path):
    url = "https://cloud-api.yandex.net/v1/disk/resources?path=" + urllib.parse.quote(path)
    req = urllib.request.Request(url, method="PUT", headers=H)
    try:
        with urllib.request.urlopen(req, timeout=60) as r:
            return True
    except urllib.error.HTTPError as e:
        return "already" in e.read().decode(errors="replace")

# 1. Создаём «Фото домов», перемещаем «Фото домов по объектам» -> «Фото домов/По объектам»
disk_mkdir("/Фото домов")
st = api_move("/Фото домов по объектам", "/Фото домов/По объектам")
print("Перемещение «По объектам»:", st)

# 2. Собираем все файлы из индекса, кроме тех, что уже в «Фото домов/По объектам»
idx = json.load(open("/root/site/video/hram_photos_index.json"))
photos = []
for folder, items in idx.items():
    for p in items:
        photos.append({"src": p[0], "name": p[1]})
print("Всего в индексе:", len(photos))

# исключаем те, что уже перенесены в «По объектам» (проверяем существование по списку кластеров)
gps_cache = json.load(open("/root/site/video/gps_cache2.json", encoding="utf-8"))
in_clusters = set()
for val in gps_cache.values():
    if val.get("gps"):
        in_clusters.add(val["name"])
# но не все с GPS перенесены — только из 5 папок. Проверим фактически: список ключей кластеров
clusters = json.load(open("/root/site/video/clusters_geocoded.json", encoding="utf-8"))
cluster_keys = set()
KEEP_DIRS = {1, 3, 5, 6, 12}
for cl in clusters:
    if cl["cluster"] in KEEP_DIRS:
        for k in cl["keys"]:
            cluster_keys.add(k)
print("Уже в «По объектам»:", len(cluster_keys))

pending = [p for p in photos if p["src"] not in cluster_keys]
print("К переносу в корень «Фото домов»:", len(pending))

# имена занятые в «По объектам» + уже запланированные
used = {p["name"] for p in photos if p["src"] in cluster_keys}
planned = {}

def unique_name(name):
    if name not in used and name not in planned:
        planned[name] = True
        return name
    base, ext = os.path.splitext(name)
    i = 2
    while True:
        cand = f"{base} ({i}){ext}"
        if cand not in used and cand not in planned:
            planned[cand] = True
            return cand
        i += 1

jobs = []
for p in pending:
    jobs.append((p["src"], p["name"], "/Фото домов/" + unique_name(p["name"])))

print(f"Задач на move: {len(jobs)}")

def work(job):
    src, orig, dst = job
    for attempt in range(3):
        st = api_move(src, dst)
        if st in (200, 201, 202):
            return (dst, True, st)
        if st == 409:
            return (dst, False, "конфликт имени (пропущен)")
        time.sleep(2)
    return (dst, False, f"HTTP {st}")

t0 = time.time()
ok = fail = 0
with ThreadPoolExecutor(max_workers=5) as ex:
    futs = [ex.submit(work, j) for j in jobs]
    for i, fut in enumerate(as_completed(futs), 1):
        dst, success, st = fut.result()
        if success: ok += 1
        else: fail += 1
        if i % 150 == 0:
            print(f"  {i}/{len(jobs)} | ok: {ok} | fail: {fail} | {time.time()-t0:.0f}с")

print(f"\nГОТОВО: перенесено {ok}, ошибок {fail}")
