import { useEffect, useRef, useState } from 'react';
import './App.css';

const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL || '').trim().replace(/\/$/, '');
const API_BASE_LABEL = API_BASE_URL || window.location.origin;

function apiUrl(path) {
  return `${API_BASE_URL}${path}`;
}

const MODE_OPTIONS = [
  { value: 'url', label: 'PDF URL' },
  { value: 'upload', label: 'Upload PDF' },
];

const VIEW_OPTIONS = [
  { value: 'query', label: 'Query' },
  { value: 'history', label: 'History' },
];

const ANSWER_STATUS_LABELS = {
  ok: 'OK',
  no_context: 'No Context',
  error: 'Error',
};

const CLAIM_VERDICT_LABELS = {
  supported: 'Supported',
  weakly_supported: 'Weakly Supported',
  unsupported: 'Unsupported',
};

function parseQuestions(text) {
  return text
    .split(/\r?\n/)
    .map((line) => line.trim())
    .filter(Boolean);
}

async function readResponseBody(response) {
  const contentType = response.headers.get('content-type') || '';

  if (contentType.includes('application/json')) {
    return response.json();
  }

  const text = await response.text();
  if (!text) {
    return null;
  }

  try {
    return JSON.parse(text);
  } catch {
    return text;
  }
}

function getErrorMessage(payload, fallbackMessage) {
  if (!payload) {
    return fallbackMessage;
  }

  if (typeof payload === 'string') {
    return payload;
  }

  if (typeof payload.detail === 'string') {
    return payload.detail;
  }

  if (Array.isArray(payload.detail)) {
    return payload.detail
      .map((item) => {
        if (typeof item === 'string') {
          return item;
        }
        if (item && typeof item.msg === 'string') {
          return item.msg;
        }
        return null;
      })
      .filter(Boolean)
      .join(' ');
  }

  if (typeof payload.message === 'string') {
    return payload.message;
  }

  return fallbackMessage;
}

function formatFileSize(bytes) {
  if (!Number.isFinite(bytes) || bytes <= 0) {
    return '';
  }

  if (bytes < 1024 * 1024) {
    return `${(bytes / 1024).toFixed(1)} KB`;
  }

  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}

function formatDateTime(value) {
  if (!value) {
    return 'Unknown date';
  }

  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return 'Unknown date';
  }

  return date.toLocaleString();
}

function formatDocumentTitle(document) {
  return document.filename || document.source_url || `Document ${document.id.slice(0, 8)}`;
}

function App() {
  const fileInputRef = useRef(null);
  const [activeView, setActiveView] = useState('query');
  const [mode, setMode] = useState('url');
  const [documentUrl, setDocumentUrl] = useState('');
  const [selectedFile, setSelectedFile] = useState(null);
  const [questionsText, setQuestionsText] = useState('');
  const [token, setToken] = useState('');
  const [showToken, setShowToken] = useState(false);
  const [answers, setAnswers] = useState([]);
  const [formError, setFormError] = useState('');
  const [requestError, setRequestError] = useState('');
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [runMeta, setRunMeta] = useState({ cacheStatus: '', cacheEntries: '' });
  const [historyDocuments, setHistoryDocuments] = useState([]);
  const [historyQueries, setHistoryQueries] = useState([]);
  const [selectedHistoryDocument, setSelectedHistoryDocument] = useState(null);
  const [historyLoading, setHistoryLoading] = useState(false);
  const [historyError, setHistoryError] = useState('');
  const [health, setHealth] = useState({
    loading: true,
    error: '',
    data: null,
  });

  const questions = parseQuestions(questionsText);

  async function refreshHealth() {
    setHealth((current) => ({
      ...current,
      loading: true,
      error: '',
    }));

    try {
      const response = await fetch(apiUrl('/health'));
      const payload = await readResponseBody(response);

      if (!response.ok) {
        throw new Error(getErrorMessage(payload, `Health check failed with status ${response.status}.`));
      }

      setHealth({
        loading: false,
        error: '',
        data: payload,
      });
    } catch (error) {
      setHealth({
        loading: false,
        error: error instanceof Error ? error.message : 'Unable to reach the backend.',
        data: null,
      });
    }
  }

  useEffect(() => {
    void refreshHealth();
  }, []);

  function getAuthHeader() {
    return { Authorization: `Bearer ${token.trim()}` };
  }

  function validateHistoryAccess() {
    if (!token.trim()) {
      setHistoryError('API token is required.');
      return false;
    }

    return true;
  }

  async function loadHistoryDocuments() {
    if (!validateHistoryAccess()) {
      return;
    }

    setHistoryLoading(true);
    setHistoryError('');
    setSelectedHistoryDocument(null);
    setHistoryQueries([]);

    try {
      const response = await fetch(apiUrl('/history/documents'), {
        headers: getAuthHeader(),
      });
      const payload = await readResponseBody(response);

      if (!response.ok) {
        throw new Error(getErrorMessage(payload, `History request failed with status ${response.status}.`));
      }

      setHistoryDocuments(Array.isArray(payload?.documents) ? payload.documents : []);
    } catch (error) {
      setHistoryError(error instanceof Error ? error.message : 'Unable to load history.');
    } finally {
      setHistoryLoading(false);
    }
  }

  async function loadHistoryQueries(document) {
    if (!validateHistoryAccess()) {
      return;
    }

    setHistoryLoading(true);
    setHistoryError('');
    setSelectedHistoryDocument(document);
    setHistoryQueries([]);

    try {
      const response = await fetch(apiUrl(`/history/documents/${encodeURIComponent(document.id)}/queries`), {
        headers: getAuthHeader(),
      });
      const payload = await readResponseBody(response);

      if (!response.ok) {
        throw new Error(getErrorMessage(payload, `History request failed with status ${response.status}.`));
      }

      setHistoryQueries(Array.isArray(payload?.queries) ? payload.queries : []);
    } catch (error) {
      setHistoryError(error instanceof Error ? error.message : 'Unable to load query history.');
    } finally {
      setHistoryLoading(false);
    }
  }

  function handleViewChange(nextView) {
    setActiveView(nextView);
    setHistoryError('');

    if (nextView === 'history' && historyDocuments.length === 0 && token.trim()) {
      void loadHistoryDocuments();
    }
  }

  function handleModeChange(nextMode) {
    setMode(nextMode);
    setFormError('');
    setRequestError('');
  }

  function handleFileChange(event) {
    const nextFile = event.target.files?.[0] || null;
    setFormError('');

    if (!nextFile) {
      setSelectedFile(null);
      return;
    }

    const isPdf = nextFile.type === 'application/pdf' || nextFile.name.toLowerCase().endsWith('.pdf');
    if (!isPdf) {
      setSelectedFile(null);
      setFormError('Please choose a PDF file.');
      event.target.value = '';
      return;
    }

    setSelectedFile(nextFile);
  }

  function handleReset() {
    setMode('url');
    setDocumentUrl('');
    setSelectedFile(null);
    setQuestionsText('');
    setToken('');
    setShowToken(false);
    setAnswers([]);
    setFormError('');
    setRequestError('');
    setRunMeta({ cacheStatus: '', cacheEntries: '' });
    setHistoryDocuments([]);
    setHistoryQueries([]);
    setSelectedHistoryDocument(null);
    setHistoryError('');

    if (fileInputRef.current) {
      fileInputRef.current.value = '';
    }
  }

  function validateForm() {
    if (!token.trim()) {
      return 'API token is required.';
    }

    if (mode === 'url' && !documentUrl.trim()) {
      return 'PDF URL is required.';
    }

    if (mode === 'upload' && !selectedFile) {
      return 'Please choose a PDF file to upload.';
    }

    if (questions.length === 0) {
      return 'Enter at least one question.';
    }

    return '';
  }

  async function handleSubmit(event) {
    event.preventDefault();

    const validationMessage = validateForm();
    if (validationMessage) {
      setFormError(validationMessage);
      return;
    }

    setFormError('');
    setRequestError('');
    setAnswers([]);
    setRunMeta({ cacheStatus: '', cacheEntries: '' });
    setIsSubmitting(true);

    try {
      const authHeader = getAuthHeader();
      let response;

      if (mode === 'url') {
        response = await fetch(apiUrl('/hackrx/run'), {
          method: 'POST',
          headers: {
            ...authHeader,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            documents: documentUrl.trim(),
            questions,
          }),
        });
      } else {
        const formData = new FormData();
        formData.append('file', selectedFile);
        formData.append('questions_json', JSON.stringify(questions));

        response = await fetch(apiUrl('/hackrx/upload-run'), {
          method: 'POST',
          headers: authHeader,
          body: formData,
        });
      }

      const payload = await readResponseBody(response);

      if (!response.ok) {
        throw new Error(getErrorMessage(payload, `Request failed with status ${response.status}.`));
      }

      setAnswers(Array.isArray(payload?.answers) ? payload.answers : []);
      setRunMeta({
        cacheStatus: response.headers.get('X-Document-Cache') || '',
        cacheEntries: response.headers.get('X-Cache-Entries') || '',
      });
      await refreshHealth();
    } catch (error) {
      setRequestError(error instanceof Error ? error.message : 'Request failed.');
    } finally {
      setIsSubmitting(false);
    }
  }

  const healthStatusClass = health.loading ? 'checking' : health.data?.status === 'healthy' ? 'healthy' : 'unhealthy';
  const healthStatusLabel = health.loading ? 'Checking' : health.data?.status === 'healthy' ? 'Healthy' : 'Unhealthy';

  return (
    <div className="app-shell">
      <header className="hero">
        <p className="eyebrow">Intelligent Document Query Engine</p>
        <h1>Ask questions over PDF documents</h1>
        <p className="hero-subtitle">
          Ask questions over PDF documents using retrieval-augmented generation.
        </p>
      </header>

      <div className="view-switch" role="tablist" aria-label="Application view">
        {VIEW_OPTIONS.map((option) => (
          <button
            key={option.value}
            type="button"
            className={`view-button ${activeView === option.value ? 'is-active' : ''}`}
            onClick={() => handleViewChange(option.value)}
          >
            {option.label}
          </button>
        ))}
      </div>

      {activeView === 'query' ? (
        <>
      <div className="top-grid">
        <form className="panel" onSubmit={handleSubmit}>
          <div className="panel-header">
            <div>
              <p className="panel-kicker">Query setup</p>
              <h2>Document input</h2>
            </div>
            <span className="subtle-copy">{questions.length} question{questions.length === 1 ? '' : 's'}</span>
          </div>

          <div className="mode-switch" role="tablist" aria-label="PDF input mode">
            {MODE_OPTIONS.map((option) => (
              <button
                key={option.value}
                type="button"
                className={`mode-button ${mode === option.value ? 'is-active' : ''}`}
                onClick={() => handleModeChange(option.value)}
              >
                {option.label}
              </button>
            ))}
          </div>

          {mode === 'url' ? (
            <label className="field">
              <span className="field-label">PDF URL</span>
              <input
                type="url"
                className="field-input"
                placeholder="https://example.com/document.pdf"
                value={documentUrl}
                onChange={(event) => setDocumentUrl(event.target.value)}
              />
            </label>
          ) : (
            <label className="field">
              <span className="field-label">Upload PDF</span>
              <input
                ref={fileInputRef}
                type="file"
                className="field-input field-input-file"
                accept=".pdf,application/pdf"
                onChange={handleFileChange}
              />
              <span className="field-help">
                {selectedFile
                  ? `${selectedFile.name} ${formatFileSize(selectedFile.size)}`.trim()
                  : 'Choose a PDF file from your machine.'}
              </span>
            </label>
          )}

          <label className="field">
            <span className="field-label">Questions</span>
            <textarea
              className="field-input field-textarea"
              placeholder={'What is this document about?\nWhat are the key dates?\nWhich sections mention exclusions?'}
              value={questionsText}
              onChange={(event) => setQuestionsText(event.target.value)}
            />
            <span className="field-help">Enter one question per line</span>
          </label>

          <label className="field">
            <span className="field-label">Bearer token</span>
            <div className="token-row">
              <input
                type={showToken ? 'text' : 'password'}
                className="field-input"
                placeholder="Paste the API token used by the backend"
                value={token}
                onChange={(event) => setToken(event.target.value)}
              />
              <button
                type="button"
                className="ghost-button"
                onClick={() => setShowToken((current) => !current)}
              >
                {showToken ? 'Hide' : 'Show'}
              </button>
            </div>
          </label>

          {formError ? <div className="notice notice-error">{formError}</div> : null}

          <div className="actions">
            <button type="submit" className="primary-button" disabled={isSubmitting}>
              {isSubmitting ? 'Running query...' : 'Run Query'}
            </button>
            <button type="button" className="secondary-button" onClick={handleReset} disabled={isSubmitting}>
              Clear
            </button>
          </div>
        </form>

        <aside className="panel status-panel">
          <div className="panel-header">
            <div>
              <p className="panel-kicker">Service status</p>
              <h2>Backend health</h2>
            </div>
            <button type="button" className="ghost-button" onClick={() => void refreshHealth()} disabled={health.loading}>
              Refresh
            </button>
          </div>

          <div className="health-card">
            <div className="health-summary">
              <span className={`status-pill status-${healthStatusClass}`}>{healthStatusLabel}</span>
              <span className="version-copy">
                {health.data?.version ? `v${health.data.version}` : 'No version available'}
              </span>
            </div>

            <dl className="health-grid">
              <div>
                <dt>API base</dt>
                <dd>{API_BASE_LABEL}</dd>
              </div>
              <div>
                <dt>Cache entries</dt>
                <dd>{health.data?.cache_entries ?? 'n/a'}</dd>
              </div>
              <div>
                <dt>Embeddings</dt>
                <dd>{health.data ? (health.data.embedding_model_loaded ? 'Loaded' : 'Idle') : 'Unknown'}</dd>
              </div>
              <div>
                <dt>Reranker</dt>
                <dd>{health.data ? (health.data.reranker_loaded ? 'Loaded' : 'Idle') : 'Unknown'}</dd>
              </div>
              <div>
                <dt>Groq client</dt>
                <dd>{health.data ? (health.data.groq_client_loaded ? 'Loaded' : 'Idle') : 'Unknown'}</dd>
              </div>
            </dl>

            {health.error ? <p className="status-message">{health.error}</p> : null}
          </div>

          <div className="support-card">
            <p className="support-title">Supported modes</p>
            <p className="support-copy">Run the existing URL workflow or upload a local PDF without changing the backend contract.</p>
          </div>
        </aside>
      </div>

      <section className="panel results-panel">
        <div className="panel-header">
          <div>
            <p className="panel-kicker">Results</p>
            <h2>Answers</h2>
          </div>
          <div className="results-meta">
            {runMeta.cacheStatus ? (
              <span>
                Cache {runMeta.cacheStatus}
                {runMeta.cacheEntries ? ` | ${runMeta.cacheEntries} entr${runMeta.cacheEntries === '1' ? 'y' : 'ies'}` : ''}
              </span>
            ) : (
              <span>Ready for a demo run</span>
            )}
          </div>
        </div>

        {requestError ? <div className="notice notice-error">{requestError}</div> : null}

        {isSubmitting ? (
          <div className="loading-box" aria-live="polite">
            <span className="spinner" aria-hidden="true" />
            Running document query...
          </div>
        ) : null}

        {!isSubmitting && !requestError && answers.length === 0 ? (
          <div className="empty-state">
            Submit a PDF URL or upload a PDF to see grounded answers with source excerpts.
          </div>
        ) : null}

        <div className="answers-grid">
          {answers.map((answer, index) => (
            <article className="answer-card" key={`${answer.question}-${index}`}>
              <div className="answer-header">
                <div>
                  <p className="section-label">Question</p>
                  <h3>{answer.question}</h3>
                </div>
                <span className={`status-pill status-${answer.status || 'error'}`}>
                  {ANSWER_STATUS_LABELS[answer.status] || answer.status || 'Unknown'}
                </span>
              </div>

              <div className="answer-copy">
                <p className="section-label">Answer</p>
                <p>{answer.answer}</p>
              </div>

              {answer.claim_verifications?.length ? (
                <div className="claim-section">
                  <div className="source-header">
                    <p className="section-label">Claim Verification</p>
                    <span className="subtle-copy">{answer.claim_verifications.length}</span>
                  </div>

                  <div className="claim-list">
                    {answer.claim_verifications.map((verification, verificationIndex) => (
                      <div className="claim-card" key={`${verification.claim}-${verificationIndex}`}>
                        <div className="claim-card-header">
                          <p className="claim-text">{verification.claim}</p>
                          <span className={`status-pill status-${verification.verdict || 'unsupported'}`}>
                            {CLAIM_VERDICT_LABELS[verification.verdict] || verification.verdict || 'Unknown'}
                          </span>
                        </div>

                        <p className="claim-rationale">{verification.rationale}</p>

                        <div className="claim-sources">
                          <div className="claim-sources-header">
                            <p className="section-label">Supporting Sources</p>
                            <span className="subtle-copy">{verification.sources?.length || 0}</span>
                          </div>

                          {verification.sources?.length ? (
                            <div className="source-list claim-source-list">
                              {verification.sources.map((source, sourceIndex) => (
                                <div className="source-card claim-source-card" key={`${source.page}-${source.chunk_id}-${sourceIndex}`}>
                                  <div className="source-meta">
                                    <span>Page {source.page}</span>
                                    <span>Chunk {source.chunk_id}</span>
                                  </div>
                                  <p>{source.excerpt}</p>
                                </div>
                              ))}
                            </div>
                          ) : (
                            <p className="empty-sources">No supporting sources were selected for this claim.</p>
                          )}
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              ) : null}

              <div className="source-section">
                <div className="source-header">
                  <p className="section-label">Sources</p>
                  <span className="subtle-copy">{answer.sources?.length || 0}</span>
                </div>

                {answer.sources?.length ? (
                  <div className="source-list">
                    {answer.sources.map((source, sourceIndex) => (
                      <div className="source-card" key={`${source.page}-${source.chunk_id}-${sourceIndex}`}>
                        <div className="source-meta">
                          <span>Page {source.page}</span>
                          <span>Chunk {source.chunk_id}</span>
                        </div>
                        <p>{source.excerpt}</p>
                      </div>
                    ))}
                  </div>
                ) : (
                  <p className="empty-sources">No source excerpts were returned for this answer.</p>
                )}
              </div>
            </article>
          ))}
        </div>
      </section>
        </>
      ) : (
        <section className="panel history-panel">
          <div className="panel-header">
            <div>
              <p className="panel-kicker">History</p>
              <h2>{selectedHistoryDocument ? 'Document queries' : 'Documents'}</h2>
            </div>
            <div className="history-actions">
              {selectedHistoryDocument ? (
                <button
                  type="button"
                  className="ghost-button"
                  onClick={() => {
                    setSelectedHistoryDocument(null);
                    setHistoryQueries([]);
                    setHistoryError('');
                  }}
                  disabled={historyLoading}
                >
                  Back
                </button>
              ) : null}
              <button
                type="button"
                className="ghost-button"
                onClick={() => void loadHistoryDocuments()}
                disabled={historyLoading}
              >
                Refresh
              </button>
            </div>
          </div>

          {selectedHistoryDocument ? (
            <div className="history-context">
              <p className="section-label">Selected document</p>
              <h3>{formatDocumentTitle(selectedHistoryDocument)}</h3>
              <p>
                {selectedHistoryDocument.source_type} | {selectedHistoryDocument.chunk_count} chunks |{' '}
                {selectedHistoryDocument.query_count} queries
              </p>
            </div>
          ) : null}

          {historyError ? <div className="notice notice-error">{historyError}</div> : null}

          {historyLoading ? (
            <div className="loading-box" aria-live="polite">
              <span className="spinner" aria-hidden="true" />
              Loading history...
            </div>
          ) : null}

          {!historyLoading && !historyError && !selectedHistoryDocument && historyDocuments.length === 0 ? (
            <div className="empty-state">No persisted documents found for the current user.</div>
          ) : null}

          {!historyLoading && !historyError && selectedHistoryDocument && historyQueries.length === 0 ? (
            <div className="empty-state">No persisted queries found for this document.</div>
          ) : null}

          {!selectedHistoryDocument ? (
            <div className="history-list">
              {historyDocuments.map((document) => (
                <button
                  type="button"
                  className="history-row"
                  key={document.id}
                  onClick={() => void loadHistoryQueries(document)}
                >
                  <span>
                    <strong>{formatDocumentTitle(document)}</strong>
                    <span className="history-meta">
                      {document.source_type} | {document.status} | {formatDateTime(document.created_at)}
                    </span>
                  </span>
                  <span className="history-counts">
                    {document.chunk_count} chunks
                    <br />
                    {document.query_count} queries
                  </span>
                </button>
              ))}
            </div>
          ) : (
            <div className="history-list">
              {historyQueries.map((query) => (
                <article className="history-query" key={query.id}>
                  <div className="history-query-header">
                    <div>
                      <p className="section-label">Question</p>
                      <h3>{query.question}</h3>
                    </div>
                    <span className={`status-pill status-${query.status || 'error'}`}>
                      {query.is_abstained ? 'Abstained' : ANSWER_STATUS_LABELS[query.status] || query.status || 'Unknown'}
                    </span>
                  </div>
                  <p>{query.answer}</p>
                  <div className="history-meta">
                    {formatDateTime(query.created_at)}
                    {Number.isFinite(query.latency_ms) ? ` | ${Math.round(query.latency_ms)} ms` : ''}
                  </div>
                </article>
              ))}
            </div>
          )}
        </section>
      )}
    </div>
  );
}

export default App;
