/**
 * TrusteeScanUploadView (UC1)
 *
 * Mobile-friendly scan/upload: photo, drag-and-drop, or file picker.
 * Uploads files, then starts the trustee pipeline (extract → process → sync)
 * via the consolidated graphicalEditor execution engine.
 *
 * The /api/workflows/ routes accept any feature instanceId the user has access to;
 * no separate graphicalEditor instance is needed.
 */

import React, { useState, useCallback, useContext, useEffect, useRef } from 'react';
import { useCurrentInstance } from '../../../hooks/useCurrentInstance';
import { useToast } from '../../../contexts/ToastContext';
import { FileContext } from '../../../contexts/FileContext';
import api from '../../../api';
import { _buildScanUploadGraph } from './trusteePipelineGraph';
import styles from './TrusteeViews.module.css';

import { useLanguage } from '../../../providers/language/LanguageContext';

const DEFAULT_EXTRACTION_PROMPT = `Extrahiere Spesendaten aus dem Dokument. Gib Dokumenttyp (INVOICE, EXPENSE_RECEIPT, …) und Datensätze zurück. CSV-Spalten: valuta,company,desc,bookingAmount,bookingCurrency,vatPercentage,vatAmount,debitAccountNumber,creditAccountNumber,tags.`;

interface UploadedEntry {
  fileId: string;
  fileName: string;
}

type PipelineState = 'idle' | 'starting' | 'running' | 'completed' | 'error';

const _parseErrorDetail = (detail: unknown): string => {
  if (typeof detail === 'string') return detail;
  if (Array.isArray(detail)) {
    return (detail as Array<{ msg?: string }>).map((e) => e.msg || JSON.stringify(e)).join(', ');
  }
  if (detail && typeof detail === 'object' && 'msg' in (detail as object)) return (detail as { msg: string }).msg;
  if (detail && typeof detail === 'object' && 'message' in (detail as object)) return (detail as { message: string }).message;
  return String(detail);
};

interface TrusteeScanUploadViewProps {
  embedded?: boolean;
}

export const TrusteeScanUploadView: React.FC<TrusteeScanUploadViewProps> = ({ embedded = false }) => {
  const { t } = useLanguage();
  const { instanceId } = useCurrentInstance();
  const { showSuccess, showError } = useToast();
  const fileContext = useContext(FileContext);
  const [uploadedFiles, setUploadedFiles] = useState<UploadedEntry[]>([]);
  const [isDragging, setIsDragging] = useState(false);
  const [isStarting, setIsStarting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [pipelineState, setPipelineState] = useState<PipelineState>('idle');
  const [pipelineSummary, setPipelineSummary] = useState<string>('');
  const [pipelineDetail, setPipelineDetail] = useState<string>('');
  const [pipelineRunId, setPipelineRunId] = useState<string | null>(null);
  const [lastPollAt, setLastPollAt] = useState<number | null>(null);
  const pollTimerRef = useRef<number | null>(null);
  const isPollingRef = useRef(false);

  const handleFileUpload = fileContext?.handleFileUpload;
  const uploadingFile = fileContext?.uploadingFile ?? false;

  const addFile = useCallback((fileId: string, fileName: string) => {
    setUploadedFiles((prev) => {
      if (prev.some((f) => f.fileId === fileId)) return prev;
      return [...prev, { fileId, fileName }];
    });
  }, []);

  const removeFile = useCallback((fileId: string) => {
    setUploadedFiles((prev) => prev.filter((f) => f.fileId !== fileId));
  }, []);

  const onFiles = useCallback(
    async (files: FileList | null) => {
      if (!files?.length || !handleFileUpload || !instanceId) return;
      setError(null);
      for (let i = 0; i < files.length; i++) {
        const file = files[i];
        const name = file.name.toLowerCase();
        if (!name.endsWith('.pdf') && !name.endsWith('.jpg') && !name.endsWith('.jpeg')) {
          showError('Invalid file', `${file.name}: only PDF and JPG are supported.`);
          continue;
        }
        const result = await handleFileUpload(file);
        if (result.success && result.fileData) {
          const data = result.fileData?.file || result.fileData;
          const id = data?.id || result.fileData?.id;
          if (id) addFile(id, file.name);
          else showError('Upload', 'Could not get file ID from server.');
        } else {
          showError('Upload failed', result.error || 'Unknown error');
        }
      }
    },
    [handleFileUpload, instanceId, showError, addFile]
  );

  const onDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      setIsDragging(false);
      onFiles(e.dataTransfer.files);
    },
    [onFiles]
  );

  const onDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(true);
  }, []);

  const onDragLeave = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(false);
  }, []);

  const onInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      onFiles(e.target.files);
      e.target.value = '';
    },
    [onFiles]
  );

  const stopPolling = useCallback(() => {
    if (pollTimerRef.current !== null) {
      window.clearInterval(pollTimerRef.current);
      pollTimerRef.current = null;
    }
    isPollingRef.current = false;
  }, []);

  const pollRunStatus = useCallback(
    async (runId: string) => {
      if (!instanceId || !runId || isPollingRef.current) return;
      isPollingRef.current = true;
      try {
        const stepsRes = await api.get(
          `/api/workflows/${instanceId}/runs/${runId}/steps`
        );
        const steps = Array.isArray(stepsRes?.data?.steps) ? stepsRes.data.steps : [];

        const completedSteps = steps.filter((s: any) => s.status === 'completed');
        const failedSteps = steps.filter((s: any) => s.status === 'failed');
        const runningSteps = steps.filter((s: any) => s.status === 'running');
        const latestStep = steps.length > 0 ? steps[steps.length - 1] : null;

        setLastPollAt(Date.now());
        setPipelineSummary(
          `Run ${runId.slice(0, 8)} — ${completedSteps.length}/${steps.length} steps completed`
        );

        if (latestStep) {
          const label = latestStep.nodeType || latestStep.nodeId || '';
          const status = latestStep.status || '';
          setPipelineDetail(`${label}: ${status}`);
        }

        if (failedSteps.length > 0) {
          const failedStep = failedSteps[failedSteps.length - 1];
          const errMsg = failedStep.error || 'Step failed';
          setPipelineState('error');
          setError(errMsg);
          stopPolling();
          showError('Pipeline error', errMsg);
          return;
        }

        if (runningSteps.length === 0 && completedSteps.length === steps.length && steps.length > 0) {
          setPipelineState('completed');
          stopPolling();
          showSuccess('Pipeline completed', 'Extraction and processing workflow finished successfully.');
          return;
        }

        setPipelineState('running');
      } catch (pollErr: any) {
        if (pollErr?.response?.status === 404) {
          setPipelineState('running');
          return;
        }
        const msg = _parseErrorDetail(pollErr.response?.data?.detail) || pollErr.message || 'Polling failed';
        setPipelineState('error');
        setError(msg);
        stopPolling();
        showError('Polling error', msg);
      } finally {
        isPollingRef.current = false;
      }
    },
    [instanceId, showError, showSuccess, stopPolling]
  );

  useEffect(() => {
    if (!instanceId || !pipelineRunId || (pipelineState !== 'running' && pipelineState !== 'starting')) {
      return;
    }

    void pollRunStatus(pipelineRunId);
    pollTimerRef.current = window.setInterval(() => {
      void pollRunStatus(pipelineRunId);
    }, 3000);

    return () => {
      stopPolling();
    };
  }, [instanceId, pipelineRunId, pipelineState, pollRunStatus, stopPolling]);

  useEffect(() => {
    return () => {
      stopPolling();
    };
  }, [stopPolling]);

  const handleStart = useCallback(async () => {
    if (!instanceId || uploadedFiles.length === 0) {
      showError('Missing data', 'Please upload at least one PDF or JPG first.');
      return;
    }
    setIsStarting(true);
    setError(null);
    setPipelineState('starting');
    setPipelineSummary('Starting pipeline workflow...');
    setPipelineDetail('');
    try {
      const fileIds = uploadedFiles.map((f) => f.fileId);
      const graph = _buildScanUploadGraph(instanceId, fileIds, DEFAULT_EXTRACTION_PROMPT);
      const response = await api.post(
        `/api/workflows/${instanceId}/execute`,
        { graph }
      );
      const runId = response?.data?.runId || null;
      if (!runId) {
        const success = response?.data?.success;
        if (success) {
          setPipelineState('completed');
          setPipelineSummary('Pipeline completed (synchronous execution).');
          showSuccess('Completed', 'Pipeline finished: Extract → Process → Sync.');
        } else {
          throw new Error(response?.data?.error || 'Workflow executed but no run ID returned.');
        }
      } else {
        setPipelineRunId(runId);
        setPipelineState('running');
        setPipelineSummary(`Run ${runId.slice(0, 8)} started. Waiting for progress updates...`);
        showSuccess('Started', 'Pipeline started: Extract → Process → Sync. You can follow progress in Workflows.');
      }
    } catch (err: any) {
      const msg = _parseErrorDetail(err.response?.data?.detail) || err.message || 'Failed to start workflow';
      setPipelineState('error');
      setError(msg);
      showError(t('Fehler'), msg);
    } finally {
      setIsStarting(false);
    }
  }, [instanceId, uploadedFiles, showSuccess, showError]);

  if (!fileContext) {
    return (
      <div className={styles.listView}>
        <p>{t('Datei-Upload ist nicht verfügbar')}</p>
      </div>
    );
  }

  const content = (
    <>
        {!embedded && <h3 className={styles.sectionTitle}>{t('Scan-Upload')}</h3>}
        <p className={styles.sectionDescription}>
          {t('Laden Sie PDF- oder JPG-Dokumente (Belege, Rechnungen) hoch. Starten Sie dann die Pipeline: Daten extrahieren → Positionen erstellen → in Buchhaltung synchronisieren.')}
        </p>
        {error && <div className={styles.errorMessage}>{error}</div>}
        {pipelineState !== 'idle' && (
          <div className={pipelineState === 'error' ? styles.errorMessage : styles.successMessage}>
            <strong>{t('Pipeline-Status')}</strong> {pipelineState}
            {pipelineSummary && <div style={{ marginTop: '0.25rem' }}>{pipelineSummary}</div>}
            {pipelineDetail && <div style={{ marginTop: '0.25rem' }}>Latest log: {pipelineDetail}</div>}
            {lastPollAt && (
              <div style={{ marginTop: '0.25rem', fontSize: '0.8125rem', opacity: 0.85 }}>
                Last update: {new Date(lastPollAt).toLocaleTimeString()}
              </div>
            )}
          </div>
        )}

        {/* Drop zone + file input */}
        <div
          onDrop={onDrop}
          onDragOver={onDragOver}
          onDragLeave={onDragLeave}
          style={{
            border: `2px dashed ${isDragging ? 'var(--primary-color, #2563eb)' : 'var(--border-color, #d0d0d0)'}`,
            borderRadius: 8,
            padding: '2rem',
            textAlign: 'center',
            background: isDragging ? 'var(--surface-color, #f0f4ff)' : 'var(--bg-secondary, #fafafa)',
            marginBottom: '1rem',
          }}
        >
          <p style={{ margin: '0 0 1rem 0', fontSize: '0.9375rem' }}>
            Drag files here or choose below. PDF and JPG only.
          </p>
          <label className={styles.primaryButton} style={{ cursor: 'pointer', display: 'inline-block' }}>
            <input
              type="file"
              accept=".pdf,.jpg,.jpeg,application/pdf,image/jpeg"
              multiple
              onChange={onInputChange}
              disabled={uploadingFile}
              style={{ display: 'none' }}
              aria-label={t('Dateien auswählen')}
            />
            {uploadingFile ? 'Uploading…' : 'Choose files'}
          </label>
          <label className={styles.secondaryButton} style={{ cursor: 'pointer', display: 'inline-block', marginLeft: '0.5rem' }}>
            <input
              type="file"
              accept="image/*"
              capture="environment"
              onChange={onInputChange}
              disabled={uploadingFile}
              style={{ display: 'none' }}
              aria-label={t('Foto aufnehmen')}
            />
            Take photo
          </label>
        </div>

        {/* Uploaded list */}
        {uploadedFiles.length > 0 && (
          <>
            <p className={styles.sectionDescription}>
              <strong>{uploadedFiles.length}</strong> file(s) ready. Click Start to run the pipeline.
            </p>
            <ul style={{ listStyle: 'none', padding: 0, margin: '0 0 1rem 0' }}>
              {uploadedFiles.map(({ fileId, fileName }) => (
                <li
                  key={fileId}
                  style={{
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between',
                    padding: '0.5rem 0',
                    borderBottom: '1px solid var(--border-color, #e0e0e0)',
                  }}
                >
                  <span style={{ fontSize: '0.875rem' }}>{fileName}</span>
                  <button
                    type="button"
                    className={styles.secondaryButton}
                    onClick={() => removeFile(fileId)}
                    style={{ padding: '0.25rem 0.5rem', fontSize: '0.8125rem' }}
                  >
                    Remove
                  </button>
                </li>
              ))}
            </ul>
            <button
              className={styles.primaryButton}
              onClick={handleStart}
              disabled={isStarting || pipelineState === 'starting' || pipelineState === 'running'}
            >
              {isStarting || pipelineState === 'starting'
                ? 'Starting...'
                : pipelineState === 'running'
                  ? t('Pipeline läuft')
                  : t('Pipeline starten')}
            </button>
          </>
        )}
    </>
  );

  if (embedded) {
    return <>{content}</>;
  }

  return (
    <div className={styles.listView}>
      <div className={styles.expenseImportSection}>
        {content}
      </div>
    </div>
  );
};

export default TrusteeScanUploadView;
