// =====================================================
// storage-cloud.jsx — Cloudflare D1 API バックエンド版
// =====================================================
// Phase 2: localStorage → Cloudflare Workers + D1
// 既存の storage.jsx と同じインターフェースを提供
// =====================================================

(function (global) {
  'use strict';

  // ---- API ベースURL（環境ごとに切替可能）----
  const API_BASE = global.__PHYSIO_API_BASE__ || 'https://physio-admin-api.yuzuman2013.workers.dev';

  // ---- ストレージキー（既存互換のため残す）----
  const KEYS = {
    BOOKINGS:           'seitai_bookings_v1',
    BLOCKS:             'seitai_blocks_v1',
    CUSTOMERS:          'seitai_customers_v1',
    MENUS:              'seitai_menus_v2',
    NOTIFICATIONS:      'seitai_notifs_v1',
    AI_LOGS:            'seitai_ai_logs_v1',
    AI_LOGS_READ:       'seitai_ai_logs_read_v1',
    SETTINGS:           'seitai_settings_v1',
    AUTH:               'seitai_auth_v1',
    NOTIFICATIONS_READ: 'seitai_notifications_read_v1',
    MOBILE_NOTIF_READ:  'seitai_mobile_notif_read_v1',
  };

  // ---- ローカルキャッシュ（オフライン耐性 + 同期版API互換）----
  const cache = {
    bookings: [],
    blocks: [],
    customers: [],
    menus: [],
    notifications: [],
    settings: {},
  };
  let _cacheReady = false;

  // 🔐 認証ヘッダー（localStorageから読む）
  function _authHeaders() {
    try {
      const t = global.localStorage && global.localStorage.getItem('seitai_api_token_v1');
      return t ? { 'Authorization': 'Bearer ' + t } : {};
    } catch (e) { return {}; }
  }
  // 🔐 401時のトークン再入力フロー（認証切れ・初回起動対策）
  // 認証エラー時：プロンプトで新トークン入力 → localStorageに保存 → 1度だけリトライ
  // フラグで「認証失敗状態」を立て、空配列での上書き保存を回避（古いキャッシュ書き戻し事故防止）
  let _authFailureMode = false;
  function _isAuthFailureMode() { return _authFailureMode; }
  async function _handle401AndRetry(url, options) {
    const cur = (function(){ try { return localStorage.getItem('seitai_api_token_v1') || ''; } catch(e){ return ''; } })();
    const msg = cur
      ? '⚠️ APIトークンが無効です。新しいトークンを入力してください：'
      : '🔐 APIトークン未設定です。トークンを入力してください（エンジニアから受け取った文字列）：';
    const newToken = prompt(msg, cur);
    if (!newToken || newToken === cur) {
      _authFailureMode = true;
      return null; // リトライしない
    }
    try { localStorage.setItem('seitai_api_token_v1', newToken.trim()); } catch(e){}
    _authFailureMode = false;
    const headers = { ...(options?.headers || {}), 'Authorization': 'Bearer ' + newToken.trim() };
    return await fetch(url, { ...options, headers });
  }
  // ---- API ヘルパー ----
  async function apiGet(path) {
    const url = `${API_BASE}${path}`;
    let r = await fetch(url, { credentials: 'omit', headers: _authHeaders() });
    if (r.status === 401) {
      const r2 = await _handle401AndRetry(url, { credentials: 'omit', headers: _authHeaders() });
      if (r2) r = r2;
    }
    if (!r.ok) throw new Error(`API GET ${path} -> ${r.status}`);
    return r.json();
  }
  async function apiPost(path, body) {
    const url = `${API_BASE}${path}`;
    const opts = {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', ..._authHeaders() },
      credentials: 'omit',
      body: JSON.stringify(body),
    };
    let r = await fetch(url, opts);
    if (r.status === 401) {
      const r2 = await _handle401AndRetry(url, opts);
      if (r2) r = r2;
    }
    if (!r.ok) throw new Error(`API POST ${path} -> ${r.status}`);
    return r.json();
  }
  async function apiPut(path, body) {
    const url = `${API_BASE}${path}`;
    const opts = {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json', ..._authHeaders() },
      credentials: 'omit',
      body: JSON.stringify(body),
    };
    let r = await fetch(url, opts);
    if (r.status === 401) {
      const r2 = await _handle401AndRetry(url, opts);
      if (r2) r = r2;
    }
    if (!r.ok) throw new Error(`API PUT ${path} -> ${r.status}`);
    return r.json();
  }
  async function apiDelete(path) {
    const url = `${API_BASE}${path}`;
    const opts = { method: 'DELETE', credentials: 'omit', headers: _authHeaders() };
    let r = await fetch(url, opts);
    if (r.status === 401) {
      const r2 = await _handle401AndRetry(url, opts);
      if (r2) r = r2;
    }
    if (!r.ok) throw new Error(`API DELETE ${path} -> ${r.status}`);
    return r.json();
  }

  // ---- マルチタブ対策：他タブの更新を検知 ----
  const TAB_ID = Math.random().toString(36).slice(2, 10);
  const TAB_EVENT_KEY = '__physio_tab_event__';
  // 他タブに「更新したよ」を通知
  function _broadcastChange(scope) {
    try {
      localStorage.setItem(TAB_EVENT_KEY, JSON.stringify({ tab: TAB_ID, scope, at: Date.now() }));
    } catch {}
  }
  // 他タブからの通知を購読 → 該当データを再フェッチ
  if (typeof window !== 'undefined') {
    window.addEventListener('storage', async (e) => {
      if (e.key !== TAB_EVENT_KEY || !e.newValue) return;
      try {
        const evt = JSON.parse(e.newValue);
        if (evt.tab === TAB_ID) return; // 自タブの通知は無視
        // 該当スコープのキャッシュを再取得
        if (evt.scope === 'bookings' || evt.scope === 'all') {
          cache.bookings = await apiGet('/api/bookings'); _notify(KEYS.BOOKINGS);
        }
        if (evt.scope === 'customers' || evt.scope === 'all') {
          cache.customers = await apiGet('/api/customers'); _notify(KEYS.CUSTOMERS);
        }
        if (evt.scope === 'menus' || evt.scope === 'all') {
          cache.menus = await apiGet('/api/menus'); _notify(KEYS.MENUS);
        }
        if (evt.scope === 'blocks' || evt.scope === 'all') {
          cache.blocks = await apiGet('/api/blocks'); _notify(KEYS.BLOCKS);
        }
        if (evt.scope === 'settings' || evt.scope === 'all') {
          cache.settings = await apiGet('/api/settings'); _notify(KEYS.SETTINGS);
        }
        console.log('[Storage Cloud] re-synced from other tab:', evt.scope);
      } catch (err) { console.warn('[Storage Cloud] tab sync error:', err); }
    });
  }

  // ---- 初期キャッシュ読み込み ----
  async function _bootstrap() {
    if (_cacheReady) return;
    try {
      const [bookings, blocks, customers, menus, notifications, settings] = await Promise.all([
        apiGet('/api/bookings'),
        apiGet('/api/blocks'),
        apiGet('/api/customers'),
        apiGet('/api/menus'),
        apiGet('/api/notifications'),
        apiGet('/api/settings'),
      ]);
      cache.bookings = bookings;
      cache.blocks = blocks;
      cache.customers = customers;
      cache.menus = menus;
      cache.notifications = notifications;
      cache.settings = settings;
      _cacheReady = true;
      _notifyAll();
      console.log('[Storage Cloud] cache loaded', {
        bookings: bookings.length,
        blocks: blocks.length,
        customers: customers.length,
        menus: menus.length,
      });
    } catch (e) {
      console.error('[Storage Cloud] bootstrap failed:', e);
      // フォールバック：localStorageから読む
      _bootstrapFromLocalStorage();
    }
  }

  function _bootstrapFromLocalStorage() {
    console.warn('[Storage Cloud] using localStorage fallback');
    try {
      cache.bookings = JSON.parse(localStorage.getItem(KEYS.BOOKINGS) || '[]');
      cache.blocks = JSON.parse(localStorage.getItem(KEYS.BLOCKS) || '[]');
      cache.customers = JSON.parse(localStorage.getItem(KEYS.CUSTOMERS) || '[]');
      cache.menus = JSON.parse(localStorage.getItem(KEYS.MENUS) || '[]');
    } catch (e) {}
    _cacheReady = true;
  }

  // ---- 変更通知 ----
  const _listeners = new Map();
  function subscribe(key, callback) {
    if (!_listeners.has(key)) _listeners.set(key, new Set());
    _listeners.get(key).add(callback);
    return () => _listeners.get(key)?.delete(callback);
  }
  function _notify(key) {
    _listeners.get(key)?.forEach(cb => { try { cb(); } catch (e) {} });
  }
  function _notifyAll() {
    Object.keys(KEYS).forEach(k => _notify(KEYS[k]));
  }

  // ---- 公開 API ----
  const Storage = {
    KEYS,
    _ready: () => _cacheReady,
    _bootstrap,

    // ===== 予約 =====
    async getBookings() { if (!_cacheReady) await _bootstrap(); return [...cache.bookings]; },
    async addBooking(b) {
      const created = await apiPost('/api/bookings', b);
      cache.bookings.unshift(created);
      _notify(KEYS.BOOKINGS);
      _broadcastChange('bookings');
      return created;
    },
    async updateBooking(id, patch) {
      const updated = await apiPut(`/api/bookings/${id}`, patch);
      const idx = cache.bookings.findIndex(x => x.id === id);
      if (idx >= 0) cache.bookings[idx] = updated;
      _notify(KEYS.BOOKINGS);
      return updated;
    },
    async deleteBooking(id) {
      await apiDelete(`/api/bookings/${id}`);
      cache.bookings = cache.bookings.filter(x => x.id !== id);
      _notify(KEYS.BOOKINGS);
      return true;
    },
    subscribeBookings(cb) { return subscribe(KEYS.BOOKINGS, cb); },

    // ===== 顧客 =====
    async getCustomers() { if (!_cacheReady) await _bootstrap(); return [...cache.customers]; },
    async addCustomer(c) {
      const created = await apiPost('/api/customers', c);
      cache.customers.unshift(created);
      _notify(KEYS.CUSTOMERS);
      return created;
    },
    async updateCustomer(id, patch) {
      const updated = await apiPut(`/api/customers/${id}`, patch);
      const idx = cache.customers.findIndex(x => x.id === id);
      if (idx >= 0) cache.customers[idx] = updated;
      _notify(KEYS.CUSTOMERS);
      return updated;
    },
    subscribeCustomers(cb) { return subscribe(KEYS.CUSTOMERS, cb); },

    // ===== 休業 =====
    async getBlocks() { if (!_cacheReady) await _bootstrap(); return [...cache.blocks]; },
    async addBlock(b) {
      const created = await apiPost('/api/blocks', b);
      cache.blocks.unshift(created);
      _notify(KEYS.BLOCKS);
      return created;
    },
    async deleteBlock(id) {
      await apiDelete(`/api/blocks/${id}`);
      cache.blocks = cache.blocks.filter(x => x.id !== id);
      _notify(KEYS.BLOCKS);
      return true;
    },
    subscribeBlocks(cb) { return subscribe(KEYS.BLOCKS, cb); },

    // ===== メニュー =====
    async getMenus() { if (!_cacheReady) await _bootstrap(); return [...cache.menus]; },
    subscribeMenus(cb) { return subscribe(KEYS.MENUS, cb); },

    // ===== 通知 =====
    async getNotifications() { if (!_cacheReady) await _bootstrap(); return [...cache.notifications]; },
    subscribeNotifications(cb) { return subscribe(KEYS.NOTIFICATIONS, cb); },

    // ===== 設定 =====
    async getSettings() { if (!_cacheReady) await _bootstrap(); return { ...cache.settings }; },
    async setSettings(s) {
      await apiPut('/api/settings', s);
      cache.settings = { ...cache.settings, ...s };
      return cache.settings;
    },

    // ===== 同期版（既存コード互換）=====
    // 既存コードは Storage.sync.get(key, fallback) を呼ぶので、キャッシュから即返す
    sync: {
      get(key, fallback) {
        if (!_cacheReady) {
          // 起動中はlocalStorageフォールバック
          try { const v = localStorage.getItem(key); return v ? JSON.parse(v) : fallback; }
          catch (e) { return fallback; }
        }
        switch (key) {
          case KEYS.BOOKINGS: return cache.bookings;
          case KEYS.BLOCKS: return cache.blocks;
          case KEYS.CUSTOMERS: return cache.customers;
          case KEYS.MENUS: return cache.menus;
          case KEYS.NOTIFICATIONS: return cache.notifications;
          case KEYS.SETTINGS: return cache.settings;
          default: {
            try { const v = localStorage.getItem(key); return v ? JSON.parse(v) : fallback; }
            catch (e) { return fallback; }
          }
        }
      },
      set(key, val) {
        // 同期版はキャッシュとlocalStorageに書く（API反映は呼び出し側で async API を使う）
        try { localStorage.setItem(key, JSON.stringify(val)); } catch (e) {}
        // 🚨 認証失敗状態のときは絶対にクラウドへ書き戻さない（古いローカルキャッシュで上書きする事故防止）
        if (_authFailureMode) {
          console.warn('[Storage Cloud] skip cloud sync — auth failure mode (token invalid)');
          return;
        }
        // 🛡️ 安全ガード：空配列で既存キャッシュを上書きしない（Reactの初期render→persist effectで起こる事故を防ぐ）
        const isEmptyArr = Array.isArray(val) && val.length === 0;
        const wipeWouldLoseData = (cur) => isEmptyArr && Array.isArray(cur) && cur.length > 0;
        if (key === KEYS.BOOKINGS) {
          if (wipeWouldLoseData(cache.bookings)) {
            console.warn('[Storage Cloud] skip wiping cache.bookings (initial empty state vs', cache.bookings.length, 'items)');
          } else {
            cache.bookings = val;
            if (_cacheReady) _syncBookingsToCloud(val);
          }
        } else if (key === KEYS.BLOCKS) {
          if (wipeWouldLoseData(cache.blocks)) {
            console.warn('[Storage Cloud] skip wiping cache.blocks');
          } else {
            cache.blocks = val;
            if (_cacheReady) _syncBlocksToCloud(val);
          }
        } else if (key === KEYS.CUSTOMERS) {
          if (wipeWouldLoseData(cache.customers)) {
            console.warn('[Storage Cloud] skip wiping cache.customers');
          } else {
            cache.customers = val;
            if (_cacheReady) _syncCustomersToCloud(val);
          }
        } else if (key === KEYS.MENUS) {
          if (wipeWouldLoseData(cache.menus)) {
            console.warn('[Storage Cloud] skip wiping cache.menus');
          } else {
            cache.menus = val;
            if (_cacheReady) _syncMenusToCloud(val);
          }
        } else if (key === KEYS.SETTINGS) {
          // 🛡️ admin形式（hours配列を含む）でない値はキャッシュ上書きしない（初期DEFAULT_SETTINGSとの混同で D1 を壊さないため）
          const isAdminShape = val && typeof val === 'object' && Array.isArray(val.hours);
          if (!isAdminShape && cache.settings && Array.isArray(cache.settings.hours)) {
            console.warn('[Storage Cloud] skip overwriting cache.settings (incoming value lacks hours array)');
          } else {
            cache.settings = val;
            if (_cacheReady && isAdminShape) _syncSettingsToCloud(val);
          }
        }
        _notify(key);
        return val;
      },
    },

    backend: 'cloudflare-d1',
    version: 2,
  };

  // ---- 一括同期（差分検出してAPI呼び出し）----
  // 既存コードは setBookings(全配列) を呼ぶので、それをAPIのCRUDに分解する
  async function _syncArrayToCloud(table, newArr, oldArr, addFn, updateFn, deleteFn) {
    // 🛡️ 安全ガード：空配列で同期しようとしているがクラウドに既存データがある場合、
    // それはほぼ確実にReactの初期化レースかバグ。誤って全削除しないようにスキップ。
    if (newArr.length === 0 && oldArr.length > 0) {
      console.warn(`[Storage Cloud] skip mass-delete sync for ${table} (newArr=[], cloud has ${oldArr.length} items)`);
      return;
    }
    const oldMap = new Map(oldArr.map(x => [x.id, x]));
    const newMap = new Map(newArr.map(x => [x.id, x]));
    const tasks = [];

    // 削除
    for (const oldItem of oldArr) {
      if (!newMap.has(oldItem.id)) tasks.push(deleteFn(oldItem.id));
    }
    // 追加・更新
    for (const newItem of newArr) {
      const oldItem = oldMap.get(newItem.id);
      if (!oldItem) {
        tasks.push(addFn(newItem));
      } else if (JSON.stringify(oldItem) !== JSON.stringify(newItem)) {
        tasks.push(updateFn(newItem.id, newItem));
      }
    }
    try {
      await Promise.all(tasks);
    } catch (e) {
      console.error('[Storage Cloud] sync error', e);
      // 失敗時はトースト通知用イベントを発行
      try {
        window.dispatchEvent(new CustomEvent('physio:cloud-error', {
          detail: { table, message: e.message || String(e) }
        }));
      } catch {}
    }
  }

  // ?force=1 を付けて衝突チェックをバイパスするブッキングID集合（夫婦・グループ施術用）
  function _consumeForceFlag(id) {
    const set = global.__PHYSIO_FORCE_BOOKING_IDS;
    if (!set || !set.has(id)) return false;
    set.delete(id);
    return true;
  }

  let _bookingsSyncing = false;
  async function _syncBookingsToCloud(newArr) {
    if (_bookingsSyncing) return;
    _bookingsSyncing = true;
    try {
      const cloud = await apiGet('/api/bookings');
      await _syncArrayToCloud('bookings', newArr, cloud,
        (b) => apiPost('/api/bookings' + (_consumeForceFlag(b.id) ? '?force=1' : ''), b),
        (id, b) => apiPut(`/api/bookings/${id}` + (_consumeForceFlag(id) ? '?force=1' : ''), b),
        (id) => apiDelete(`/api/bookings/${id}`));
      _broadcastChange('bookings');
    } finally { _bookingsSyncing = false; }
  }

  let _blocksSyncing = false;
  async function _syncBlocksToCloud(newArr) {
    if (_blocksSyncing) return;
    _blocksSyncing = true;
    try {
      const cloud = await apiGet('/api/blocks');
      await _syncArrayToCloud('blocks', newArr, cloud,
        (b) => apiPost('/api/blocks', b),
        (id, b) => apiPut(`/api/blocks/${id}`, b),
        (id) => apiDelete(`/api/blocks/${id}`));
      _broadcastChange('blocks');
    } finally { _blocksSyncing = false; }
  }

  let _customersSyncing = false;
  async function _syncCustomersToCloud(newArr) {
    if (_customersSyncing) return;
    _customersSyncing = true;
    try {
      const cloud = await apiGet('/api/customers');
      await _syncArrayToCloud('customers', newArr, cloud,
        (c) => apiPost('/api/customers', c),
        (id, c) => apiPut(`/api/customers/${id}`, c),
        (id) => apiDelete(`/api/customers/${id}`));
      _broadcastChange('customers');
    } finally { _customersSyncing = false; }
  }

  let _menusSyncing = false;
  async function _syncMenusToCloud(newArr) {
    if (_menusSyncing) return;
    _menusSyncing = true;
    try {
      const cloud = await apiGet('/api/menus');
      await _syncArrayToCloud('menus', newArr, cloud,
        (m) => apiPost('/api/menus', m),
        (id, m) => apiPut(`/api/menus/${id}`, m),
        (id) => apiDelete(`/api/menus/${id}`));
      _broadcastChange('menus');
    } finally { _menusSyncing = false; }
  }

  // ---- settings の同期（オブジェクト全体をPUT）----
  let _settingsSyncing = false;
  async function _syncSettingsToCloud(s) {
    if (_settingsSyncing) return;
    _settingsSyncing = true;
    try {
      await apiPut('/api/settings', s);
    } catch (e) {
      console.error('[Storage Cloud] settings sync error:', e);
    } finally {
      _settingsSyncing = false;
    }
  }

  // ---- グローバル公開 + 起動 ----
  global.Storage = Storage;
  console.log('[Storage] initialized (backend: cloudflare-d1, version: 2)');

  // 自動でキャッシュ読み込み開始
  if (typeof window !== 'undefined') {
    _bootstrap();
  }
})(typeof window !== 'undefined' ? window : globalThis);
