// 診断20問フォームの React コンポーネント
// Babel Standalone 経由で評価されるため ESM ではなく React/ReactDOM をグローバルから取り出す
const { useState, useEffect, useMemo, useRef } = React;
const { createRoot } = ReactDOM;

const PAGE_SIZE = 5;
// 4件法。表示順は左端 (あてはまらない) → 右端 (あてはまる)
// value: 採点用スコア（+2/+1/-1/-2）。中央は存在しないので reverse_question:true なら符号反転
// size: A 案デザインの両端強・中間弱 (likert-circle.size-strong / size-soft)
const CHOICES = [
  { value: '-2', label: '全くあてはまらない', size: 'strong' },
  { value: '-1', label: 'あまりあてはまらない', size: 'soft' },
  { value: '1',  label: 'あてはまる',           size: 'soft' },
  { value: '2',  label: '非常にあてはまる',     size: 'strong' }
];

// answers: { questionId: '-2'|'-1'|'1'|'2' }
// questions: [{id, axis, reverse_question, text}, ...]
// axes: [{key, positive, negative}, ...]
// 戻り値: 4文字の型コード（例 "IPLC"）
const calculateTypeCode = (answers, questions, axes) => {
  const sums = {};
  axes.forEach((axis) => { sums[axis.key] = 0; });
  questions.forEach((question) => {
    const raw = answers[question.id];
    if (raw === undefined || raw === null || raw === '') return;
    let contribution = Number(raw);
    if (question.reverse_question) contribution = -contribution;
    const axis = axes.find((a) => a.key === question.axis);
    if (!axis) return;
    sums[axis.key] += contribution;
  });
  return axes.map((axis) => (sums[axis.key] > 0 ? axis.positive : axis.negative)).join('');
};

const csrfToken = () => {
  const meta = document.querySelector('meta[name="csrf-token"]');
  return meta ? meta.content : '';
};

// Fisher-Yates シャッフル。元配列は変更せず、新しい配列を返す
const shuffleArray = (array) => {
  const result = array.slice();
  for (let i = result.length - 1; i > 0; i -= 1) {
    const j = Math.floor(Math.random() * (i + 1));
    [result[i], result[j]] = [result[j], result[i]];
  }
  return result;
};

const DiagnosisForm = ({ container }) => {
  const questionsUrl = container.dataset.questionsUrl;
  const mode = container.dataset.mode;
  const redirectBase = container.dataset.redirectBase;
  const submitUrl = container.dataset.submitUrl;

  const [questions, setQuestions] = useState(null);
  const [axes, setAxes] = useState(null);
  const [answers, setAnswers] = useState({});
  const [loadError, setLoadError] = useState(null);
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState(null);
  const [pageIndex, setPageIndex] = useState(0);
  const [pageError, setPageError] = useState(null);
  const topRef = useRef(null);

  useEffect(() => {
    let cancelled = false;
    fetch(questionsUrl, { headers: { Accept: 'application/json' } })
      .then((res) => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
      })
      .then((data) => {
        if (cancelled) return;
        setQuestions(shuffleArray(data.questions || []));
        setAxes(data.axes || []);
      })
      .catch((err) => {
        if (cancelled) return;
        setLoadError(err.message || '読み込みに失敗しました');
      });
    return () => { cancelled = true; };
  }, [questionsUrl]);

  const pages = useMemo(() => {
    if (!questions) return [];
    const result = [];
    for (let i = 0; i < questions.length; i += PAGE_SIZE) {
      result.push(questions.slice(i, i + PAGE_SIZE));
    }
    return result;
  }, [questions]);

  const totalPages = pages.length;
  const isLastPage = totalPages > 0 && pageIndex === totalPages - 1;
  const isFirstPage = pageIndex === 0;

  const answeredCount = useMemo(() => {
    if (!questions) return 0;
    return questions.reduce((acc, q) => acc + (answers[q.id] ? 1 : 0), 0);
  }, [answers, questions]);

  const totalCount = questions ? questions.length : 0;
  const progressPercent = totalCount === 0 ? 0 : Math.round((answeredCount / totalCount) * 100);

  const handleChange = (questionId, value) => {
    setAnswers((prev) => ({ ...prev, [questionId]: value }));
    setPageError(null);
  };

  const scrollToTop = () => {
    if (topRef.current && typeof topRef.current.scrollIntoView === 'function') {
      topRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }
  };

  const currentPageAllAnswered = () => {
    const currentQuestions = pages[pageIndex] || [];
    return currentQuestions.every((q) => !!answers[q.id]);
  };

  const handleNext = () => {
    if (!currentPageAllAnswered()) {
      setPageError('このページの設問すべてにお答えください');
      return;
    }
    setPageError(null);
    setPageIndex((idx) => Math.min(idx + 1, totalPages - 1));
    scrollToTop();
  };

  const handlePrev = () => {
    setPageError(null);
    setPageIndex((idx) => Math.max(idx - 1, 0));
    scrollToTop();
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    if (submitting || !questions || !axes) return;
    if (!currentPageAllAnswered()) {
      setPageError('このページの設問すべてにお答えください');
      return;
    }
    const unanswered = questions.some((q) => !answers[q.id]);
    if (unanswered) {
      setPageError('全ての設問にお答えください');
      return;
    }
    const code = calculateTypeCode(answers, questions, axes);
    if (mode === 'update') {
      setSubmitting(true);
      setSubmitError(null);
      fetch(submitUrl, {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
          'X-CSRF-Token': csrfToken()
        },
        credentials: 'same-origin',
        body: JSON.stringify({ type_code: code })
      })
        .then(async (res) => {
          const data = await res.json().catch(() => ({}));
          if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
          if (data.redirect_to) {
            window.location.assign(data.redirect_to);
          } else {
            window.location.reload();
          }
        })
        .catch((err) => {
          setSubmitting(false);
          setSubmitError(err.message || '更新に失敗しました');
        });
    } else {
      window.location.assign(`${redirectBase}${code}`);
    }
  };

  if (loadError) {
    return (
      <div className="alert alert-danger d-flex align-items-center" role="alert">
        <i className="bi bi-exclamation-triangle-fill me-2" aria-hidden="true"></i>
        <div>質問の読み込みに失敗しました: {loadError}</div>
      </div>
    );
  }
  if (!questions || !axes) {
    return (
      <div className="d-flex align-items-center text-ink-soft py-4">
        <div className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></div>
        読み込み中...
      </div>
    );
  }

  const currentQuestions = pages[pageIndex] || [];
  const baseQuestionNumber = pageIndex * PAGE_SIZE;

  return (
    <form onSubmit={handleSubmit} noValidate>
      <div ref={topRef} />

      {/* プログレス */}
      <div className="mb-3">
        <div className="d-flex justify-content-between font-serif text-ink-soft tracking-wide mb-2"
             style={{ fontSize: '0.7rem' }}>
          <span>PAGE {pageIndex + 1} / {totalPages}</span>
          <span>{answeredCount} / {totalCount} ({progressPercent}%)</span>
        </div>
        <div className="diagnosis-progress" role="progressbar"
             aria-label="診断の進捗"
             aria-valuenow={progressPercent}
             aria-valuemin="0"
             aria-valuemax="100">
          <div className="bar" style={{ width: `${progressPercent}%` }}></div>
        </div>
      </div>

      <div className="d-flex flex-column gap-3">
        {currentQuestions.map((question, indexOnPage) => {
          const number = baseQuestionNumber + indexOnPage + 1;
          const numberLabel = `Q.${String(number).padStart(2, '0')}`;
          return (
            <div key={question.id}
                 className="bg-paper border border-rule position-relative"
                 style={{ padding: '22px 16px 18px' }}>
              <div className="position-absolute bg-navy text-gold-light font-serif fw-semibold"
                   style={{ top: '-1px', left: '-1px', fontSize: '0.9rem',
                            letterSpacing: '0.15em', padding: '4px 10px' }}>
                {numberLabel}
              </div>
              <fieldset className="border-0 p-0 m-0">
                <legend className="visually-hidden">{numberLabel}</legend>
                <p className="text-ink mb-0" style={{ marginTop: '10px', lineHeight: 1.7 }}>{question.text}</p>

                <div className="likert-scale mt-3">
                  <div className="end-label left">あてはまらない</div>
                  <div className="options">
                    {CHOICES.map((choice) => {
                      const inputId = `q-${question.id}-${choice.value}`;
                      const checked = answers[question.id] === choice.value;
                      return (
                        <React.Fragment key={choice.value}>
                          <input
                            type="radio"
                            className="btn-check"
                            name={`answers[${question.id}]`}
                            id={inputId}
                            value={choice.value}
                            checked={checked}
                            onChange={() => handleChange(question.id, choice.value)}
                            autoComplete="off"
                          />
                          <label
                            htmlFor={inputId}
                            className={`likert-circle size-${choice.size}`}
                            title={choice.label}
                            aria-label={choice.label}
                          ></label>
                        </React.Fragment>
                      );
                    })}
                  </div>
                  <div className="end-label right">あてはまる</div>
                </div>
              </fieldset>
            </div>
          );
        })}
      </div>

      {pageError && (
        <div className="alert alert-warning d-flex align-items-center mt-3" role="alert">
          <i className="bi bi-exclamation-circle-fill me-2" aria-hidden="true"></i>
          <div>{pageError}</div>
        </div>
      )}
      {submitError && (
        <div className="alert alert-danger d-flex align-items-center mt-3" role="alert">
          <i className="bi bi-exclamation-triangle-fill me-2" aria-hidden="true"></i>
          <div>{submitError}</div>
        </div>
      )}

      <div className="d-flex justify-content-between align-items-stretch gap-2 mt-4">
        <button
          type="button"
          className="btn btn-outline-navy flex-grow-1"
          onClick={handlePrev}
          disabled={isFirstPage || submitting}
          style={{ flexBasis: 0 }}
        >
          ‹ 戻る
        </button>
        {isLastPage ? (
          <button type="submit" className="btn btn-navy"
                  disabled={submitting}
                  style={{ flex: '1.5 1 0' }}>
            {submitting ? (
              <>
                <span className="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
                送信中...
              </>
            ) : (
              <>{mode === 'update' ? '診断結果を保存する ›' : '結果を見る ›'}</>
            )}
          </button>
        ) : (
          <button type="button" className="btn btn-navy"
                  onClick={handleNext}
                  style={{ flex: '1.5 1 0' }}>
            次へ ›
          </button>
        )}
      </div>
    </form>
  );
};

const initializeDiagnosisForms = () => {
  document.querySelectorAll('[data-diagnosis-form]').forEach((el) => {
    if (el.dataset.diagnosisFormMounted === 'true') return;
    el.dataset.diagnosisFormMounted = 'true';
    try {
      const root = createRoot(el);
      root.render(<DiagnosisForm container={el} />);
    } catch (error) {
      console.error('Error rendering DiagnosisForm:', error);
    }
  });
};

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initializeDiagnosisForms);
} else {
  initializeDiagnosisForms();
}
