﻿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_test/downloads"
SCHEDULE_DIR = "/var/www/html/schedule_test/kot"
PLACE_DIR = "/var/www/html/schedule_test/place"
MASTER_CSV = "/var/www/html/schedule_test/master.csv"

KOT_URL = "https://s3.ta.kingoftime.jp/admin"
OUTPUT_ENCODING = "cp932"


# ====== 共通ユーティリティ ======
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")

    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


    # ==============================================================
    # データの突合・フィルタリング・マッピング処理
    # ==============================================================

    # ===== 1. マスタCSVの読み込み =====
    # 有効な社員のIDリストと、その人の「表示順」「KOT順」を保存する辞書
    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:
                    # マスターの列: 0:emp_id, 1:name, 2:disp_col, 3:disp_order, 4:kot_order, 5:is_deleted
                    if len(row) >= 6 and row[5] == "0":
                        emp_id = row[0].strip()
                        active_emp_ids.add(emp_id)
                        
                        disp_order = row[3].strip() # 4列目: 表示順
                        kot_order  = row[4].strip() # 5列目: KOT順
                        master_info_map[emp_id] = (disp_order, kot_order)
                        
            print(f"マスタ読み込み完了: 有効な社員 {len(active_emp_ids)}名")
        except Exception as e:
            print(f"マスタCSV読み込みエラー: {e}")
    else:
        print("マスタCSVが見つかりません。")

    # ===== 2. KOTダウンロードデータの読み込み =====
    l, enc_l = read_csv_any_encoding(download_csv)
    try:
        m, enc_m = read_csv_any_encoding(schedule_csv)
    except Exception:
        m = [row[:] for row in l]

    # ===== 3. KOTデータの転記（2～7列目） =====
    cnt = min(len(m), len(l))
    for i in range(cnt):
        # 15列構成に拡張しておく（14列目=disp_order, 15列目=kot_order用）
        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]

    # ===== 4. Place CSVからのデータ転記 (場所・備考・携帯・内線) =====
    place_csv_path = os.path.join(PLACE_DIR, f"place_{today_yyyymmdd}.csv")
    if os.path.exists(place_csv_path):
        try:
            place_data_map = {}
            with open(place_csv_path, mode="r", encoding="utf-8-sig", newline="") as f:
                reader = csv.reader(f)
                next(reader, None)
                for row in reader:
                    if len(row) >= 3:
                        # place_YYYYMMDD.csv -> 1:場所, 2:備考, 3:携帯, 4:内線
                        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     # 10列目: 備考
                    row[10] = v_ext      # 11列目: 内線番号
                    row[11] = v_mobile   # 12列目: 携帯番号
                    row[12] = v_place    # 13列目: 場所
        except Exception as e:
            print(f"Place CSV処理エラー: {e}")
    else:
        print(f"Placeファイルが見つかりません。場所や備考は空欄になります: {place_csv_path}")

    # ===== 5. マスタ突合（退職者除外 ＆ 14, 15列目のマッピング） =====
    final_m = []
    
    if active_emp_ids:
        # KOTデータは1行目から社員データなのでそのままループ
        for row in m:
            if len(row) > 0:
                emp_id = row[0].strip()
                
                # is_deleted=0の有効な社員だけを残す
                if emp_id in active_emp_ids:
                    # マスタ情報の辞書から「表示順」「KOT順」を取得（無ければ空欄）
                    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

    # ===== 6. 最終保存（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

    print("完了")

if __name__ == "__main__":
    main()