// Gotanda Guide — import modal & helpers
// Lets the user replace/append the built-in spot data via JSON paste,
// JSON/CSV file, or per-spot image upload.

const { useState: useImpState, useRef: useImpRef } = React;

// CSV columns (header row required, order flexible):
//   id, cat, name, name_en, tag, tag_en, desc, desc_en,
//   price, hours, hours_en, walk, rating, lat, lng,
//   address, address_en, image, tags
const CSV_HEADERS = [
  'id','cat','name','name_en','tag','tag_en','desc','desc_en',
  'price','hours','hours_en','walk','rating','lat','lng',
  'address','address_en','image','tags',
];

// minimal CSV parser: handles quoted fields with commas + escaped quotes ("")
function parseCSV(text) {
  const rows = [];
  let row = [], cell = '', inQ = false, i = 0;
  while (i < text.length) {
    const ch = text[i];
    if (inQ) {
      if (ch === '"' && text[i + 1] === '"') { cell += '"'; i += 2; continue; }
      if (ch === '"') { inQ = false; i++; continue; }
      cell += ch; i++; continue;
    }
    if (ch === '"') { inQ = true; i++; continue; }
    if (ch === ',') { row.push(cell); cell = ''; i++; continue; }
    if (ch === '\n' || ch === '\r') {
      if (ch === '\r' && text[i + 1] === '\n') i++;
      row.push(cell); rows.push(row); row = []; cell = ''; i++; continue;
    }
    cell += ch; i++;
  }
  if (cell.length || row.length) { row.push(cell); rows.push(row); }
  return rows.filter((r) => r.some((c) => c.trim().length));
}

function rowsToSpots(rows) {
  if (!rows.length) return [];
  const head = rows[0].map((h) => h.trim().toLowerCase());
  const idx = (k) => head.indexOf(k);
  const out = [];
  for (let i = 1; i < rows.length; i++) {
    const r = rows[i];
    const get = (k) => (idx(k) >= 0 ? (r[idx(k)] || '').trim() : '');
    const name_ja = get('name'); if (!name_ja && !get('name_en')) continue;
    const name_en = get('name_en') || name_ja;
    const id = get('id') || (name_ja || name_en).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') + '-' + i;
    out.push({
      id,
      cat: get('cat') || 'sight',
      name: { ja: name_ja || name_en, en: name_en },
      tag: { ja: get('tag') || '', en: get('tag_en') || get('tag') || '' },
      desc: { ja: get('desc') || '', en: get('desc_en') || get('desc') || '' },
      price: get('price') || '¥¥',
      hours: { ja: get('hours') || '', en: get('hours_en') || get('hours') || '' },
      walk: parseInt(get('walk') || '5', 10) || 5,
      rating: parseFloat(get('rating') || '4.0') || 4.0,
      coords: [parseFloat(get('lat')) || 35.6262, parseFloat(get('lng')) || 139.7232],
      address: { ja: get('address') || '', en: get('address_en') || get('address') || '' },
      image: get('image') || '',
      tags: (get('tags') || '').split(/[;,]/).map((s) => s.trim()).filter(Boolean),
      img: i % 12,
    });
  }
  return out;
}

// Normalize JSON input — accepts our internal shape OR Google Places-like rows.
function normalizeJSON(data) {
  if (!Array.isArray(data)) {
    if (data && Array.isArray(data.spots)) data = data.spots;
    else if (data && Array.isArray(data.results)) data = data.results;
    else throw new Error('Expected an array of spots.');
  }
  return data.map((d, i) => {
    // tolerate Google Places shape
    if (d.geometry && d.geometry.location) {
      return {
        id: d.place_id || d.id || `imp-${i}`,
        cat: 'sight',
        name: { ja: d.name || '', en: d.name || '' },
        tag: { ja: (d.types || [])[0] || '', en: (d.types || [])[0] || '' },
        desc: { ja: d.vicinity || '', en: d.vicinity || '' },
        price: '¥'.repeat(Math.max(1, d.price_level || 2)),
        hours: { ja: d.opening_hours?.weekday_text?.[0] || '', en: '' },
        walk: 5,
        rating: d.rating || 4.0,
        coords: [d.geometry.location.lat, d.geometry.location.lng],
        address: { ja: d.formatted_address || d.vicinity || '', en: '' },
        image: d.photo_url || d.image || '',
        tags: d.types || [],
        img: i % 12,
      };
    }
    // tolerate our own shape with strings instead of {ja,en}
    const j = (v) => (typeof v === 'string' ? { ja: v, en: v } : v || { ja: '', en: '' });
    return {
      id: d.id || `imp-${i}`,
      cat: d.cat || d.category || 'sight',
      name: j(d.name),
      tag: j(d.tag),
      desc: j(d.desc || d.description),
      price: d.price || '¥¥',
      hours: j(d.hours),
      walk: d.walk ?? 5,
      rating: d.rating ?? 4.0,
      coords: d.coords || [d.lat || 35.6262, d.lng || 139.7232],
      address: j(d.address),
      image: d.image || d.photo || d.photo_url || '',
      tags: d.tags || [],
      img: i % 12,
    };
  });
}

const SAMPLE_JSON = `[
  {
    "id": "demo-1",
    "cat": "izakaya",
    "name": { "ja": "新規店舗A", "en": "New Spot A" },
    "tag": { "ja": "東口", "en": "East Exit" },
    "desc": { "ja": "店舗の説明...", "en": "Description..." },
    "price": "¥¥",
    "hours": { "ja": "17:00–24:00", "en": "5pm–midnight" },
    "walk": 4, "rating": 4.5,
    "coords": [35.6262, 139.7232],
    "address": { "ja": "品川区東五反田 1-1", "en": "1-1 Higashi-Gotanda" },
    "image": "https://example.com/photo.jpg",
    "tags": ["Sake", "Counter"]
  }
]`;

const SAMPLE_CSV = `id,cat,name,name_en,tag,tag_en,desc,desc_en,price,hours,hours_en,walk,rating,lat,lng,address,address_en,image,tags
demo-1,izakaya,新規店舗A,New Spot A,東口,East Exit,店舗の説明,Description,¥¥,17:00–24:00,5pm–midnight,4,4.5,35.6262,139.7232,品川区東五反田 1-1,1-1 Higashi-Gotanda,https://example.com/photo.jpg,Sake;Counter`;

function ImportModal({ lang, onClose, onApply, onReset, hasCustom, currentCount, categories, onCategoriesChange, sponsors, onSponsorsChange }) {
  const [tab, setTab] = useImpState('places');
  const [text, setText] = useImpState('');
  const [mode, setMode] = useImpState('replace'); // 'replace' | 'append'
  const [error, setError] = useImpState('');
  const [info, setInfo] = useImpState('');
  const fileRef = useImpRef(null);

  const T = (ja, en) => (lang === 'ja' ? ja : en);

  const apply = () => {
    setError(''); setInfo('');
    if (!text.trim()) { setError(T('データが空です', 'No data provided')); return; }
    try {
      let spots;
      if (tab === 'json') {
        spots = normalizeJSON(JSON.parse(text));
      } else {
        spots = rowsToSpots(parseCSV(text));
      }
      if (!spots.length) { setError(T('読み込めるスポットがありませんでした', 'No spots could be parsed')); return; }
      onApply(spots, mode);
      setInfo(T(`${spots.length}件を${mode === 'replace' ? '置換' : '追加'}しました`, `${spots.length} spots ${mode === 'replace' ? 'replaced' : 'added'}`));
      setTimeout(onClose, 600);
    } catch (e) {
      setError(String(e.message || e));
    }
  };

  const onFile = (e) => {
    const f = e.target.files?.[0];
    if (!f) return;
    const isCSV = /\.csv$/i.test(f.name);
    setTab(isCSV ? 'csv' : 'json');
    const r = new FileReader();
    r.onload = () => setText(String(r.result || ''));
    r.readAsText(f);
  };

  const useSample = () => setText(tab === 'json' ? SAMPLE_JSON : SAMPLE_CSV);

  return (
    <div className="modal-bd" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()} style={{ maxWidth: 640 }}>
        <div className="modal-grip"></div>
        <div className="modal-content" style={{ paddingTop: 18 }}>
          <div className="modal-tag">{T('インポート', 'IMPORT')}</div>
          <div className="modal-title" style={{ marginBottom: 6 }}>
            {T('店舗データを読み込む', 'Import spot data')}
          </div>
          <div style={{ color: 'var(--fg-mid)', fontSize: 13, lineHeight: 1.6, marginBottom: 16 }}>
            {T(
              'JSON または CSV を貼り付け／アップロードして、現在のスポットを置換または追加できます。データはブラウザに保存されます。',
              'Paste or upload JSON / CSV to replace or append spots. Data is stored in your browser only.'
            )}
          </div>

          <div className="imp-tabs">
            <button className={tab === 'places' ? 'is-on' : ''} onClick={() => setTab('places')}>Google Places</button>
            <button className={tab === 'json' ? 'is-on' : ''} onClick={() => setTab('json')}>JSON</button>
            <button className={tab === 'csv' ? 'is-on' : ''} onClick={() => setTab('csv')}>CSV</button>
            <button className={tab === 'cats' ? 'is-on' : ''} onClick={() => setTab('cats')}>{T('カテゴリ', 'Categories')}</button>
            <button className={tab === 'sponsors' ? 'is-on' : ''} onClick={() => setTab('sponsors')}>{T('パートナー', 'Partners')}</button>
          </div>

          {tab === 'cats' && (
            <CategoryManager
              lang={lang}
              categories={categories}
              onChange={onCategoriesChange}
              onClose={onClose}
            />
          )}
          {tab === 'sponsors' && (
            <SponsorManager
              lang={lang}
              sponsors={sponsors || []}
              onChange={onSponsorsChange}
              onClose={onClose}
            />
          )}

          {tab === 'cats' || tab === 'sponsors' ? null : tab === 'places' ? (
            <GooglePlacesPanel
              lang={lang}
              categories={categories}
              onApply={(spots, m) => {
                onApply(spots, m);
                setInfo(T(`${spots.length}件を取り込みました`, `${spots.length} spots imported`));
                setTimeout(onClose, 600);
              }}
              embedded
            />
          ) : (
            <>
              <textarea
                className="imp-textarea"
                placeholder={tab === 'json' ? '[{ "name": ... }]' : 'id,cat,name,...'}
                value={text}
                onChange={(e) => setText(e.target.value)}
                spellCheck={false}
              />

              <div className="imp-actions-row">
                <button className="imp-link" onClick={useSample}>
                  {T('サンプルを挿入', 'Insert sample')}
                </button>
                <button className="imp-link" onClick={() => fileRef.current?.click()}>
                  {T('ファイルを選択', 'Choose file')} (.json/.csv)
                </button>
                <input
                  type="file" accept=".json,.csv,application/json,text/csv"
                  ref={fileRef}
                  onChange={onFile}
                  style={{ display: 'none' }}
                />
              </div>

              <div className="imp-mode">
                <label>
                  <input type="radio" name="impmode" checked={mode === 'replace'} onChange={() => setMode('replace')} />
                  {T('既存を置換', 'Replace existing')}
                </label>
                <label>
                  <input type="radio" name="impmode" checked={mode === 'append'} onChange={() => setMode('append')} />
                  {T('既存に追加', 'Append to existing')}
                </label>
                <span className="imp-count">
                  {T('現在', 'Current')}: <b>{currentCount}</b>
                </span>
              </div>
            </>
          )}

          {tab !== 'cats' && tab !== 'sponsors' && error && <div className="imp-msg imp-err">⚠ {error}</div>}
          {tab !== 'cats' && tab !== 'sponsors' && info && <div className="imp-msg imp-ok">✓ {info}</div>}
        </div>
        {tab !== 'cats' && tab !== 'sponsors' && (
          <div className="modal-actions" style={{ marginTop: 6 }}>
            {tab !== 'places' && (
              <button className="btn-primary" onClick={apply}>
                {T('適用', 'Apply')}
              </button>
            )}
            {hasCustom && (
              <button className="btn-secondary" onClick={() => { onReset(); onClose(); }}>
                {T('既定に戻す', 'Reset')}
              </button>
            )}
            <button className="btn-secondary" onClick={onClose}>
              {T('閉じる', 'Close')}
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

// Per-spot image picker — used inside the SpotModal.
function ImagePicker({ lang, value, onChange }) {
  const ref = useImpRef(null);
  const T = (ja, en) => (lang === 'ja' ? ja : en);
  const onFile = (e) => {
    const f = e.target.files?.[0];
    if (!f) return;
    const r = new FileReader();
    r.onload = () => onChange(String(r.result || ''));
    r.readAsDataURL(f);
  };
  const onDrop = (e) => {
    e.preventDefault();
    const f = e.dataTransfer.files?.[0];
    if (!f || !/^image\//.test(f.type)) return;
    const r = new FileReader();
    r.onload = () => onChange(String(r.result || ''));
    r.readAsDataURL(f);
  };
  return (
    <button
      className="imp-pic-btn"
      onClick={() => ref.current?.click()}
      onDragOver={(e) => e.preventDefault()}
      onDrop={onDrop}
      title={T('画像をアップロード（ドラッグ＆ドロップ可）', 'Upload image (drag & drop OK)')}
    >
      <span>📷</span>
      <span>{value ? T('画像を変更', 'Change photo') : T('写真を追加', 'Add photo')}</span>
      <input
        type="file" accept="image/*" ref={ref}
        style={{ display: 'none' }}
        onChange={onFile}
      />
    </button>
  );
}

Object.assign(window, { ImportModal, ImagePicker, parseCSV, rowsToSpots, normalizeJSON });
