﻿import os
import time
import csv
import shutil
import datetime

from selenium import webdriver
from selenium.common.exceptions import ElementClickInterceptedException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC


# ====== 設定 ======
KOT_URL = "https://s3.ta.kingoftime.jp/admin/"

# BATで set KOT_LOGIN_ID / set KOT_LOGIN_PASS を設定する想定（推奨）
LOGIN_ID = os.environ.get("KOT_LOGIN_ID", "3ic1998")
LOGIN_PASS = os.environ.get("KOT_LOGIN_PASS", "pts2129")

#DOWNLOAD_DIR = r"C:\Users\Administrator\Downloads"
#SCHEDULE_DIR = r"C:\Program Files\FileMaker\FileMaker Server\HTTPServer\conf\schedule\kot"
DOWNLOAD_DIR = "/var/www/html/schedule/downloads"
SCHEDULE_DIR = "/var/www/html/schedule/kot"

# ★ 現状CSVがANSI(Shift-JIS)なので、読み書きは cp932 に寄せる
ENC = "cp932"


# ====== 共通関数 ======
def wait_click(driver, by, value, timeout=20):
    el = WebDriverWait(driver, timeout).until(EC.presence_of_element_located((by, value)))
    try:
        driver.execute_script("arguments[0].scrollIntoView({block:'center'});", el)
    except Exception:
        pass

    WebDriverWait(driver, timeout).until(EC.element_to_be_clickable((by, value)))
    try:
        el.click()
    except (ElementClickInterceptedException, Exception):
        driver.execute_script("arguments[0].click();", el)
    return el


def set_date_value(driver, element_id: str, value_yyyy_mm_dd: str, timeout=10):
    """カレンダーUI被り回避：JSでvalue直入れ＋イベント発火＋ESCで閉じる"""
    el = WebDriverWait(driver, timeout).until(EC.presence_of_element_located((By.ID, element_id)))
    try:
        driver.execute_script("arguments[0].scrollIntoView({block:'center'});", el)
    except Exception:
        pass

    driver.execute_script(
        """
        const el = arguments[0];
        const v  = arguments[1];
        el.value = v;
        el.dispatchEvent(new Event('input',  {bubbles:true}));
        el.dispatchEvent(new Event('change', {bubbles:true}));
        """,
        el, value_yyyy_mm_dd
    )

    # UIを閉じる（クリック阻害防止）
    try:
        el.send_keys(Keys.ESCAPE)
    except Exception:
        pass
    try:
        driver.find_element(By.TAG_NAME, "body").send_keys(Keys.ESCAPE)
    except Exception:
        pass


def wait_download_complete(target_path: str, timeout_sec=240):
    """ダウンロード完了待ち：本体存在 & .crdownload消滅"""
    temp = target_path + ".crdownload"
    t0 = time.time()
    while time.time() - t0 < timeout_sec:
        if os.path.exists(target_path) and os.path.getsize(target_path) > 0 and (not os.path.exists(temp)):
            return True
        time.sleep(1)
    return False


def js_click_by_text(driver, text: str) -> bool:
    """画面上の『次へ/はじめる/閉じる』等を、文字ベースでJSクリック（Shadow DOMも可能範囲で探索）"""
    script = r"""
    const target = arguments[0];

    function isVisible(el){
      if(!el) return false;
      const r = el.getBoundingClientRect();
      if(r.width === 0 || r.height === 0) return false;
      const s = window.getComputedStyle(el);
      if(s.visibility === 'hidden' || s.display === 'none' || s.pointerEvents === 'none') return false;
      return true;
    }

    function tryClick(root){
      const selectors = [
        'button','a','div[role="button"]','span[role="button"]','input[type="button"]','input[type="submit"]'
      ];
      for(const sel of selectors){
        const nodes = root.querySelectorAll(sel);
        for(const el of nodes){
          let t = '';
          if(el.tagName === 'INPUT') t = (el.value || '').trim();
          else t = (el.innerText || el.textContent || '').trim();

          if(t && (t === target || t.includes(target))){
            if(isVisible(el)){
              el.scrollIntoView({block:'center', inline:'center'});
              el.click();
              return true;
            }
          }
        }
      }
      return false;
    }

    function walk(node){
      if(tryClick(node)) return true;
      const all = node.querySelectorAll('*');
      for(const el of all){
        if(el.shadowRoot){
          if(walk(el.shadowRoot)) return true;
        }
      }
      return false;
    }

    return walk(document);
    """
    try:
        return bool(driver.execute_script(script, text))
    except Exception:
        return False


def dismiss_kot_popups(driver, max_rounds=20):
    """チュートリアル（次へ/はじめる）と更新通知（閉じる）を文字クリックで潰す（iframe1階層も）"""
    def exists_text(text):
        try:
            driver.find_element(By.XPATH, f"//*[contains(normalize-space(.),'{text}')]")
            return True
        except Exception:
            return False

    for _ in range(max_rounds):
        did = False

        # default content
        for __ in range(15):
            if js_click_by_text(driver, "次へ"):
                did = True
                time.sleep(0.25)
            else:
                break

        if js_click_by_text(driver, "はじめる"):
            did = True
            time.sleep(0.25)

        if js_click_by_text(driver, "閉じる"):
            did = True
            t0 = time.time()
            while time.time() - t0 < 6:
                if not exists_text("新機能がリリースされました"):
                    break
                time.sleep(0.2)

        # iframe 1階層探索
        frames = driver.find_elements(By.TAG_NAME, "iframe")
        if frames:
            for fr in frames:
                try:
                    driver.switch_to.frame(fr)

                    for __ in range(15):
                        if js_click_by_text(driver, "次へ"):
                            did = True
                            time.sleep(0.25)
                        else:
                            break

                    if js_click_by_text(driver, "はじめる"):
                        did = True
                        time.sleep(0.25)

                    if js_click_by_text(driver, "閉じる"):
                        did = True
                        time.sleep(0.25)

                except Exception:
                    pass
                finally:
                    driver.switch_to.default_content()

        if not did:
            break


def read_csv_cp932(path):
    with open(path, newline="", encoding=ENC) as f:
        return list(csv.reader(f))


def write_csv_cp932(path, rows):
    with open(path, "w", newline="", encoding=ENC) as f:
        writer = csv.writer(f, quoting=csv.QUOTE_ALL)
        writer.writerows(rows)

def read_csv_any_encoding(path):
    """UTF-8(BOMあり/なし)とCP932を自動判別して読み込む"""
    for enc in ["utf-8-sig", "cp932", "utf-8"]:
        try:
            with open(path, mode="r", encoding=enc, newline="") as f:
                return list(csv.reader(f)), enc
        except UnicodeDecodeError:
            continue
    # 判別不能時はバイナリで強引に開くかエラー
    raise RuntimeError(f"ファイル {path} の文字コードを判別できませんでした。")
    
# ====== メイン ======
def main():
    if not LOGIN_ID or not LOGIN_PASS:
        raise RuntimeError("環境変数 KOT_LOGIN_ID / KOT_LOGIN_PASS が未設定です。")

    now = datetime.datetime.now()
    yt = now - datetime.timedelta(days=1)

    today_str = now.strftime("%Y/%m/%d")
    today2 = now.strftime("%Y%m%d")
    ytd = yt.strftime("%Y%m%d")

    download_csv = os.path.join(DOWNLOAD_DIR, f"{today2}_{today2}.csv")
    schedule_today_csv = os.path.join(SCHEDULE_DIR, f"{today2}_{today2}.csv")
    schedule_yesterday_csv = os.path.join(SCHEDULE_DIR, f"{ytd}_{ytd}.csv")

    # Chrome設定（ダウンロード先固定）
#    options = webdriver.ChromeOptions()
#    options.add_argument("--start-maximized")
    options = webdriver.ChromeOptions()
    options.add_argument("--headless=new") # 画面なしモード（必須）
    options.add_argument("--no-sandbox") # root等での実行制限を回避（必須）
    options.add_argument("--disable-dev-shm-usage") # メモリ不足によるクラッシュ回避（必須）
    options.add_argument("--window-size=1920,1080") # maximizedの代わりに解像度を指定
    prefs = {
        "download.default_directory": DOWNLOAD_DIR,
        "download.prompt_for_download": False,
        "download.directory_upgrade": True,
        "safebrowsing.enabled": True,
    }
    options.add_experimental_option("prefs", prefs)

    driver = webdriver.Chrome(options=options)
    wait = WebDriverWait(driver, 25)

    try:
        driver.get(KOT_URL)

        # ログイン
        wait.until(EC.presence_of_element_located((By.NAME, "login_id")))
        driver.find_element(By.NAME, "login_id").send_keys(LOGIN_ID)
        driver.find_element(By.NAME, "login_password").send_keys(LOGIN_PASS)
        wait_click(driver, By.ID, "login_button", timeout=25)

        # ログイン後：モーダル類を潰す
        wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
        time.sleep(1.0)
        dismiss_kot_popups(driver, max_rounds=25)

        # インポート/エクスポート
        dismiss_kot_popups(driver, max_rounds=6)
        wait_click(driver, By.ID, "select_export_type_link", timeout=30)

        # 日別データ
        dismiss_kot_popups(driver, max_rounds=6)
        wait_click(driver, By.ID, "button_2-1", timeout=30)

        # 範囲指定
        dismiss_kot_popups(driver, max_rounds=6)
        wait_click(driver, By.ID, "action_02", timeout=30)

        dismiss_kot_popups(driver, max_rounds=6)

        # 日付を当日に設定（夜間処理でも「当日のCSV」を作る想定）
        set_date_value(driver, "export_date_picker", today_str)
        set_date_value(driver, "end_export_date_picker", today_str)

        # フォーカス外し
        try:
            driver.find_element(By.TAG_NAME, "body").click()
        except Exception:
            pass

        # データ出力
        wait_click(driver, By.ID, "button_01", timeout=30)

        dismiss_kot_popups(driver, max_rounds=6)

        # 区切り文字コロン
        cut = wait.until(EC.presence_of_element_located((By.ID, "select_2")))
        Select(cut).select_by_value("2")

        # データ出力（申請中/エラー分岐）
        ex = driver.find_elements(By.ID, "export_date")
        if len(ex) > 0:
            wait_click(driver, By.ID, "export_date", timeout=25)
            wait_click(driver, By.ID, "button_01", timeout=25)
        else:
            wait_click(driver, By.ID, "button_01", timeout=25)

        # ダウンロード待ち
        if not wait_download_complete(download_csv, timeout_sec=240):
            raise RuntimeError(f"CSVダウンロードが完了しませんでした: {download_csv}")

        # ログアウト（失敗しても続行）
        try:
            wait_click(driver, By.LINK_TEXT, "ログアウト", timeout=10)
        except Exception:
            pass

    except Exception:
        # 失敗時スクショ
        try:
            driver.save_screenshot(os.path.join(SCHEDULE_DIR, "kot_night_error.png"))
        except Exception:
            pass
        raise

    finally:
        try:
            driver.quit()
        except Exception:
            pass

    # ===== ダウンロードCSVをscheduleへ移動 =====
    # 既に同名があれば上書き
    if os.path.exists(schedule_today_csv):
        try:
            os.remove(schedule_today_csv)
        except Exception:
            pass
    shutil.move(download_csv, schedule_today_csv)

    # ===== 前日CSV → 当日CSVへ：8〜14列をコピー =====
    if not os.path.exists(schedule_yesterday_csv):
        raise RuntimeError(f"前日CSVが見つかりません: {schedule_yesterday_csv}")

# ===== 前日CSV → 当日CSVへ：データの引き継ぎ =====
    l_data, enc_l = read_csv_any_encoding(schedule_yesterday_csv)  # 前日
    m_data, enc_m = read_csv_any_encoding(schedule_today_csv)      # 当日

    for i in range(len(m_data)):
        # 1. まず当日の行を15列まで確実に拡張（表示崩れ防止）
        while len(m_data[i]) < 15:
            m_data[i].append("")
        
        # 2. 前日のデータが存在する行の処理
        if i < len(l_data):
            prev_row = l_data[i]
            
            # --- 3列目（氏名 / index 2）の引き継ぎ ---
            # 当日の値に関係なく、前日の値をそのまま代入する
            if len(prev_row) >= 3:
                m_data[i][2] = prev_row[2]

            # --- 8〜14列目（index 7〜14）の引き継ぎ ---
            for col in range(8, 15):
                # 11列目(index 10)と14列目(index 13)は「場所と備考」なのでクリア
                # ※PHPの $data[10], $data[13] に合わせて調整
                if col in [9, 12]:
                    m_data[i][col] = ""
                else:
                    # それ以外の管理項目（電話番号や予備列など）を引き継ぐ
                    if col < len(prev_row):
                        m_data[i][col] = prev_row[col]

    # 最終保存：Shift-JIS (cp932)
    write_csv_cp932(schedule_today_csv, m_data)
    print(f"KOT CSVの列数を15列に統一し、データの引き継ぎを完了しました: {schedule_today_csv}")

    # ==========================================================
    # 以降、Place CSVの作成処理（既存のまま）
    # ==========================================================
    # ==========================================================
    # 追加：place_YYYYMMDD.csv の作成と特定列のクリア
    # ==========================================================
    #PLACE_DIR = r"C:\Program Files\FileMaker\FileMaker Server\HTTPServer\conf\schedule\place"
    PLACE_DIR = "/var/www/html/schedule/place"
    
    # 前日のファイル名と当日のファイル名を生成
    # ※datetimeは既にインポートされている前提
    today_dt = datetime.date.today()
    yesterday_dt = today_dt - datetime.timedelta(days=1)
    
    place_src = os.path.join(PLACE_DIR, f"place_{yesterday_dt.strftime('%Y%m%d')}.csv")
    place_dst = os.path.join(PLACE_DIR, f"place_{today_dt.strftime('%Y%m%d')}.csv")

    if os.path.exists(place_src):
        place_rows = []
        # 送信元がUTF-8とのことなので、encoding='utf-8'で処理
        with open(place_src, 'r', encoding='utf-8', newline='') as f:
            reader = list(csv.reader(f))
            if len(reader) > 0:
                # ヘッダーをコピー
                place_rows.append(reader[0])
                # 2行目以降のデータをクリアして追加
                for i in range(1, len(reader)):
                    row = reader[i]
                    if len(row) >= 3:
                        row[1] = ""  # 選択中の場所をクリア
                        row[2] = ""  # 備考をクリア
                    place_rows.append(row)

        # 当日分としてUTF-8で保存
        with open(place_dst, 'w', encoding='utf-8', newline='') as f:
            writer = csv.writer(f)
            writer.writerows(place_rows)
        print(f"Place CSVを作成しました: {place_dst}")
    else:
        print(f"前日のPlaceファイルが見つかりません: {place_src}")

#   if __name__ == "__main__":
#    main()

    print("夜間処理 完了")


if __name__ == "__main__":
    main()
