﻿import time
import os
import csv
import datetime

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, 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


# ====== 設定 ======
LOGIN_ID = os.environ.get("KOT_LOGIN_ID", "3ic1998")
LOGIN_PASS = os.environ.get("KOT_LOGIN_PASS", "pts2129")

# ★ テスト環境用のパス（本番時は /schedule/ に戻してください）
DOWNLOAD_DIR = "/var/www/html/schedule/downloads"
SCHEDULE_DIR = "/var/www/html/schedule/kot"
PLACE_DIR = "/var/www/html/schedule/place"
MASTER_CSV = "/var/www/html/schedule/master.csv"

KOT_URL = "https://s3.ta.kingoftime.jp/admin"

# ====== 共通ユーティリティ ======
def wait_click(driver, by, value, timeout=15):
    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):
    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
    )
    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=180):
    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:
    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=15):
    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
        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)
        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_any_encoding(path):
    encodings = ["cp932", "shift_jis", "utf-8-sig", "utf-8"]
    last_err = None
    for enc in encodings:
        try:
            with open(path, newline="", encoding=enc) as f:
                return list(csv.reader(f)), enc
        except UnicodeDecodeError as e:
            last_err = e
            continue
    raise last_err

def main():
    if not LOGIN_ID or not LOGIN_PASS:
        raise RuntimeError("環境変数 KOT_LOGIN_ID / KOT_LOGIN_PASS が未設定です。")

    options = webdriver.ChromeOptions()
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--window-size=1920,1080")

    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, 20)

    now = datetime.datetime.now()
    today_str = now.strftime("%Y/%m/%d")
    today_yyyymmdd = now.strftime("%Y%m%d")

    download_csv = os.path.join(DOWNLOAD_DIR, f"{today_yyyymmdd}_{today_yyyymmdd}.csv")
    schedule_csv = os.path.join(SCHEDULE_DIR, f"{today_yyyymmdd}_{today_yyyymmdd}.csv")

    # ==============================================================
    # 0. Place CSV の翌日繰り越し処理（ここで今日のファイルを作ります）
    # ==============================================================
    yesterday_dt = now - 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_yyyymmdd}.csv")

    if os.path.exists(place_src):
        try:
            place_rows = []
			# 昨日のファイルを開いて中身をすべて読み込む
            with open(place_src, 'r', encoding='utf-8-sig', newline='') as f:
                reader = csv.reader(f)
                for i, row in enumerate(reader):
                    # i > 0 でヘッダー行を除外し、要素が2つ以上ある場合のみ「場所(2列目)」をクリア
                    if i > 0 and len(row) > 1:
                        row[1] = ""
                    place_rows.append(row)

            # 新しい「今日の日付のファイル」として書き出す
            with open(place_dst, 'w', encoding='utf-8-sig', newline='') as f:
                writer = csv.writer(f)
                writer.writerows(place_rows)
            print(f"Place CSVをそのまま繰り越し作成しました: {place_dst}")
        except Exception as e:
            print(f"Place CSV繰り越しエラー: {e}")
    else:
        print(f"前日のPlaceファイルが見つかりません: {place_src}")


    # ==============================================================
    # 1. KOTから本日のデータをダウンロード
    # ==============================================================
    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=20)

        wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
        time.sleep(1.0)
        dismiss_kot_popups(driver, max_rounds=20)

        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)
        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_error.png"))
        except Exception:
            pass
        raise
    finally:
        try:
            driver.quit()
        except Exception:
            pass


    # ==============================================================
    # 2. データの突合・マッピング
    # ==============================================================
    active_emp_ids = set()
    master_info_map = {}
    
    if os.path.exists(MASTER_CSV):
        try:
            with open(MASTER_CSV, mode="r", encoding="utf-8", newline="") as f:
                reader = csv.reader(f)
                next(reader, None)
                for row in reader:
                    if len(row) >= 6 and row[5] == "0":
                        emp_id = row[0].strip()
                        active_emp_ids.add(emp_id)
                        master_info_map[emp_id] = (row[3].strip(), row[4].strip())
        except Exception as e:
            print(f"マスタCSV読み込みエラー: {e}")

    l, enc_l = read_csv_any_encoding(download_csv)
    m = [row[:] for row in l]

    # KOTデータの転記（2～7列目）
    for i in range(len(m)):
        while len(m[i]) < 15:
            m[i].append("")
        m[i][3] = l[i][3]
        m[i][4] = l[i][4]
        m[i][5] = l[i][5]
        m[i][6] = l[i][6]
        m[i][7] = l[i][7]

    # Place CSVからのデータ転記
    if os.path.exists(place_dst):
        try:
            place_data_map = {}
            with open(place_dst, mode="r", encoding="utf-8-sig", newline="") as f:
                reader = csv.reader(f)
                next(reader, None)
                for row in reader:
                    if len(row) >= 3:
                        v_place  = row[1].strip()
                        v_memo   = row[2].strip()
                        v_mobile = row[3].strip() if len(row) >= 4 else ""
                        v_ext    = row[4].strip() if len(row) >= 5 else "" 
                        place_data_map[row[0].strip()] = (v_place, v_memo, v_mobile, v_ext)

            for row in m:
                if not row: continue
                emp_id = row[0].strip()
                if emp_id in place_data_map:
                    v_place, v_memo, v_mobile, v_ext = place_data_map[emp_id]
                    row[9]  = v_memo     # 備考
                    row[10] = v_ext      # 内線
                    row[11] = v_mobile   # 携帯
                    row[12] = v_place    # 場所
        except Exception as e:
            print(f"Place CSV処理エラー: {e}")

    # マスタ突合（退職者除外 ＆ 14, 15列目のマッピング）
    final_m = []
    if active_emp_ids:
        for row in m:
            if len(row) > 0:
                emp_id = row[0].strip()
                if emp_id in active_emp_ids:
                    disp_order, kot_order = master_info_map.get(emp_id, ("", ""))
                    row[13] = disp_order # 14列目: 表示順
                    row[14] = kot_order  # 15列目: KOT順
                    final_m.append(row)
    else:
        final_m = m

    # 最終保存（Shift-JIS）
    with open(schedule_csv, "w", newline="", encoding="cp932") as f:
        writer = csv.writer(f, quoting=csv.QUOTE_ALL)
        writer.writerows(final_m)
    
    print(f"夜間バッチ完了: マスタ情報の適用と保存を行いました: {schedule_csv}")

    try:
        if os.path.exists(download_csv):
            os.remove(download_csv)
    except Exception:
        pass

if __name__ == "__main__":
    main()