/**
 * TrusteeScanUploadView (UC1)
 *
 * Mobile-friendly scan/upload: photo, drag-and-drop, or file picker.
 * Uploads files, then starts the trustee pipeline (extract → process → sync) with fileIds.
 */

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 styles from './TrusteeViews.module.css';

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);
};

export const TrusteeScanUploadView: React.FC = () => {
  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 [pipelineWorkflowId, setPipelineWorkflowId] = useState<string | null>(null);
  const [lastPollAt, setLastPollAt] = useState<number | null>(null);
  const pollTimerRef = useRef<number | null>(null);
  const latestTimestampRef = 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 buildTemplate = useCallback(
    (fileIds: string[]) => ({
      overview: 'Trustee pipeline from uploaded files',
      tasks: [
        {
          id: 'Task01',
          title: 'Extract, process, sync',
          objective: 'Run trustee pipeline on uploaded files',
          actionList: [
            {
              execMethod: 'trustee',
              execAction: 'extractFromFiles',
              execParameters: {
                fileIds,
                featureInstanceId: instanceId,
                prompt: DEFAULT_EXTRACTION_PROMPT,
              },
              execResultLabel: 'extract_result',
            },
            {
              execMethod: 'trustee',
              execAction: 'processDocuments',
              execParameters: { documentList: [], featureInstanceId: instanceId },
              execResultLabel: 'process_result',
            },
            {
              execMethod: 'trustee',
              execAction: 'syncToAccounting',
              execParameters: { documentList: [], featureInstanceId: instanceId },
              execResultLabel: 'sync_result',
            },
          ],
        },
      ],
    }),
    [instanceId]
  );

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

  const pollWorkflowStatus = useCallback(
    async (workflowId: string) => {
      if (!instanceId || !workflowId || isPollingRef.current) return;
      isPollingRef.current = true;
      try {
        const chatDataRes = await api.get(
          `/api/automations/${instanceId}/workflows/${workflowId}/chatData`,
          {
            params: latestTimestampRef.current
              ? { afterTimestamp: latestTimestampRef.current }
              : undefined,
          }
        );

        const chatItems = Array.isArray(chatDataRes?.data?.items)
          ? chatDataRes.data.items
          : [];

        const latestCreatedAt = chatItems.reduce((acc: number, item: any) => {
          const createdAt = Number(item?.createdAt || 0);
          return createdAt > acc ? createdAt : acc;
        }, latestTimestampRef.current || 0);
        if (latestCreatedAt > 0) {
          latestTimestampRef.current = latestCreatedAt;
        }

        const logMessages = chatItems
          .filter((item: any) => item?.type === 'log')
          .map((item: any) =>
            (item?.item?.message || item?.item?.status || '').toString().trim()
          )
          .filter((msg: string) => msg.length > 0);
        const latestLog = logMessages.length
          ? logMessages[logMessages.length - 1]
          : '';
        if (latestLog) {
          setPipelineDetail(latestLog);
        }

        const statItems = chatItems.filter((item: any) => item?.type === 'stat');
        const latestStat =
          statItems.length > 0 ? statItems[statItems.length - 1]?.item : null;
        const rawStatus = (
          latestStat?.status || 'running'
        ).toString().toLowerCase();

        const messageItems = chatItems.filter(
          (item: any) => item?.type === 'message'
        );
        const completionMessage = messageItems.find(
          (item: any) =>
            (item?.item?.message || '').toString().toLowerCase().startsWith('completed:')
        );

        const isCompleted =
          rawStatus === 'completed' ||
          rawStatus === 'stopped' ||
          !!completionMessage;

        const totalLogs = logMessages.length;
        const totalMessages = messageItems.length;
        setPipelineSummary(
          `Workflow ${workflowId.slice(0, 8)} — ${totalMessages} message(s), ${totalLogs} log(s)`
        );
        setLastPollAt(Date.now());

        if (isCompleted) {
          setPipelineState('completed');
          stopPolling();
          showSuccess(
            'Pipeline completed',
            'Extraction and processing workflow finished successfully.'
          );
          return;
        }
        if (rawStatus === 'error') {
          setPipelineState('error');
          stopPolling();
          if (latestLog) {
            setError(latestLog);
          }
          showError(
            'Pipeline error',
            latestLog || 'Workflow ended with status "error".'
          );
          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);
        setPipelineSummary(`Workflow status polling failed: ${msg}`);
        showError('Polling error', msg);
        stopPolling();
      } finally {
        isPollingRef.current = false;
      }
    },
    [instanceId, showError, showSuccess, stopPolling]
  );

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

    void pollWorkflowStatus(pipelineWorkflowId);
    pollTimerRef.current = window.setInterval(() => {
      void pollWorkflowStatus(pipelineWorkflowId);
    }, 3000);

    return () => {
      stopPolling();
    };
  }, [instanceId, pipelineWorkflowId, pipelineState, pollWorkflowStatus, 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('');
    latestTimestampRef.current = null;
    try {
      const fileIds = uploadedFiles.map((f) => f.fileId);
      const template = buildTemplate(fileIds);
      const prompt = `<!--TEMPLATE_PLAN_START-->\n${JSON.stringify(template)}\n<!--TEMPLATE_PLAN_END-->`;
      const response = await api.post(
        `/api/automations/${instanceId}/start`,
        { prompt },
        { params: { workflowMode: 'Automation' } }
      );
      const workflowId = response?.data?.id || response?.data?.workflowId || null;
      if (!workflowId) {
        throw new Error('Workflow started but no workflow ID was returned by backend.');
      }
      setPipelineWorkflowId(workflowId);
      setPipelineState('running');
      setPipelineSummary(`Workflow ${workflowId.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('Error', msg);
    } finally {
      setIsStarting(false);
    }
  }, [instanceId, uploadedFiles, buildTemplate, showSuccess, showError]);

  if (!fileContext) {
    return (
      <div className={styles.listView}>
        <p>File upload is not available in this context.</p>
      </div>
    );
  }

  return (
    <div className={styles.listView}>
      <div className={styles.expenseImportSection}>
        <h3 className={styles.sectionTitle}>Scan / Upload</h3>
        <p className={styles.sectionDescription}>
          Upload PDF or JPG documents (receipts, invoices). Then start the pipeline: extract data → create positions → sync to accounting.
        </p>
        {error && <div className={styles.errorMessage}>{error}</div>}
        {pipelineState !== 'idle' && (
          <div className={pipelineState === 'error' ? styles.errorMessage : styles.successMessage}>
            <strong>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="Choose files"
            />
            {uploadingFile ? 'Uploading…' : 'Choose files'}
          </label>
          {/* Mobile: optional camera capture */}
          <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="Take photo"
            />
            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'
                  ? 'Pipeline running...'
                  : 'Start pipeline'}
            </button>
          </>
        )}
      </div>
    </div>
  );
};

export default TrusteeScanUploadView;
