// 파일 ✨/mnt/d/D/_NR_/__AIWK_COMM_St/aiwk/AIWK_COMMON/core/yw-domain-loader.js
// 버전: v124-domain-uri-folder-rule
(function(){
  "use strict";
  const AIWK = window.AIWK || (window.AIWK = {});
  const VERSION = "v124-domain-uri-folder-rule";
  const STANDARD_FILES = ["ai_read.md","index.json.js","index.js","loading.js","list.js","call.js","get.js","put.js","run.js","chat.js","ping.js","panel.html","style.css","i18n.ko.js"];
  // v098: scan 단계에서는 자주 없는 선택 파일(get/put/run/chat/ping 등)을 직접 fetch하지 않는다.
  // 없는 chrome-extension://... 리소스를 fetch하면 catch해도 콘솔에 ERR_FILE_NOT_FOUND가 남기 때문이다.
  const SCAN_FILES = ["ai_read.md","index.json.js","index.js","list.js","call.js","panel.html","style.css","i18n.ko.js"];
  const EXECUTABLE_JS = {"index.json.js":1,"index.js":1,"loading.js":1,"list.js":1,"call.js":1,"get.js":1,"put.js":1,"run.js":1,"chat.js":1,"ping.js":1,"i18n.ko.js":1};
  const CSS_FILES = {"style.css":1};

  function getRuntimeUrl(path){
    if(AIWK.getUrl) return AIWK.getUrl(path);
    if(typeof chrome !== "undefined" && chrome.runtime && chrome.runtime.getURL) return chrome.runtime.getURL(path);
    return path;
  }

  function registry(){
    return window.AIWK_DOMAIN_REGISTRY || {};
  }

  function normalizePath(path){
    return String(path || "").replace(/^\/+/, "").replace(/\\/g, "/");
  }

  function registryHas(path){
    path = normalizePath(path);
    const at = path.lastIndexOf("/");
    const base = at >= 0 ? path.slice(0, at) : "";
    const name = at >= 0 ? path.slice(at + 1) : path;
    const files = registry()[base];
    return Array.isArray(files) && files.indexOf(name) >= 0;
  }

  function registryFileNames(base){
    base = normalizePath(base);
    const files = registry()[base];
    return Array.isArray(files) ? files.slice() : [];
  }

  async function exists(path){
    // v099: chrome-extension:// missing file fetch는 콘솔 404를 남긴다.
    // 따라서 scan/resolve는 빌드 시 생성된 AIWK_DOMAIN_REGISTRY만 신뢰한다.
    return registryHas(path);
  }

  async function readText(path){
    const res = await fetch(getRuntimeUrl(path), {cache:"no-store"});
    if(!res.ok) throw new Error("AIWK_DOMAIN_READ_FAIL: " + path + " status=" + res.status);
    return await res.text();
  }

  function safeKey(base){
    return String(base || "").replace(/^AIWK_COMMON\/domains\/?/, "").replace(/\/$/, "") || "domains";
  }

  async function scan(url){
    const ctx = AIWK.urlContext && AIWK.urlContext.makeContext ? AIWK.urlContext.makeContext(url || location.href) : {ok:0, href:String(url||location.href), candidate_folders:["AIWK_COMMON/domains"], lookup_folders:["AIWK_COMMON/domains"]};
    const inheritFolders = ctx.candidate_folders || [];
    const lookupFolders = ctx.lookup_folders || inheritFolders.slice().reverse();
    const levels = [];
    const effective = {};

    for (const base of inheritFolders) {
      const files = [];
      const names = registryFileNames(base).filter(function(name){ return SCAN_FILES.indexOf(name) >= 0; });
      for (const name of names) {
        const p = base + "/" + name;
        const item = {name:name, path:p, kind: CSS_FILES[name] ? "css" : (EXECUTABLE_JS[name] ? "js" : "text")};
        files.push(item);
        effective[name] = item; // 하위 폴더가 상위 파일을 override 한다.
      }
      if (files.length) levels.push({base_path:base, key:safeKey(base), files:files});
    }

    return {
      ok:1,
      version:VERSION,
      matched:levels.length ? 1 : 0,
      url:ctx,
      representative_domain:ctx.host || "",
      service_or_group:ctx.host || "",
      folder_route:ctx.folder_route || ((ctx.path_parts||[]).join("/")),
      hash_route:ctx.hash_route || "",
      inherit_levels:levels,
      loaded_levels:levels,
      lookup_levels:lookupFolders.map(function(p){ return {base_path:p, key:safeKey(p)}; }),
      effective_files:Object.keys(effective).map(function(k){ return effective[k]; }),
      modules:[],
      message: levels.length ? "URI_FOLDER_FILES_FOUND" : "NO_URI_DOMAIN_FILES_FOUND"
    };
  }

  // 가장 구체적인 URL 폴더부터 domains/ 루트까지 올라가며 파일 하나를 찾는다.
  async function resolve(fileName, url){
    const ctx = AIWK.urlContext && AIWK.urlContext.makeContext ? AIWK.urlContext.makeContext(url || location.href) : {lookup_folders:["AIWK_COMMON/domains"]};
    const folders = ctx.lookup_folders || [];
    for (const base of folders) {
      const p = base + "/" + fileName;
      if (await exists(p)) return {ok:1, path:p, base_path:base, key:safeKey(base), url:ctx};
    }
    return {ok:0, path:"", base_path:"", key:"", url:ctx, reason:"not_found"};
  }

  async function scanCurrentAndStore(){
    const result = await scan(location.href);
    if(AIWK.state && AIWK.state.set) AIWK.state.set("domain.current", result);
    try { window.dispatchEvent(new CustomEvent("aiwk_domain_scan", {detail:result})); } catch(e) {}
    return result;
  }

  AIWK.domainLoader = {
    version:VERSION,
    standardFiles:STANDARD_FILES.slice(),
    fixedFiles:STANDARD_FILES.slice(),
    scan:scan,
    resolve:resolve,
    exists:exists,
    readText:readText,
    scanCurrentAndStore:scanCurrentAndStore,
    buildDomainLevels:function(url){
      const ctx = AIWK.urlContext.makeContext(url || location.href);
      return {parts:ctx, levels:(ctx.candidate_folders||[]).map(function(p){return {base_path:p,key:safeKey(p)};}), lookup_levels:(ctx.lookup_folders||[]).map(function(p){return {base_path:p,key:safeKey(p)};})};
    },
    readLocalTabSettings:function(tabId){ try{ return JSON.parse(localStorage.getItem("AIWK_DOMAIN_TAB_SETTINGS_V065:"+String(tabId||"global"))||"{\"modules\":{}}"); }catch(e){ return {modules:{}}; } },
    writeLocalTabSettings:function(tabId,data){ localStorage.setItem("AIWK_DOMAIN_TAB_SETTINGS_V065:"+String(tabId||"global"), JSON.stringify(data||{modules:{}})); return data; },
    tabSettingsKey:function(tabId){ return "AIWK_DOMAIN_TAB_SETTINGS_V065:"+String(tabId||"global"); }
  };
}());
