import { useState, memo } from 'react';
import { isFileBlob, type FileBlob } from '@/lib/4nk/models/Data';
import { iframeUrl } from "@/app/page";
import MessageBus from '@/lib/4nk/MessageBus';

interface BlockState {
  commited_in: string;
  state_id: string;
  pcd_commitment: Record<string, string>;
  public_data: Record<string, any>;
}

interface Block {
  states: BlockState[];
}

interface Processes {
  [key: string]: Block;
}

interface ProcessesViewerProps {
  processes: Processes | null;
  myProcesses: string[];
  onProcessesUpdate?: (processes: Processes) => void;
}

const compareStates = (
  currentState: BlockState,
  index: number,
  previousState?: BlockState,
  currentPrivateData?: Record<string, any>,
  previousPrivateData?: Record<string, any>
) => {
  const result: Record<string, { 
    value: any, 
    status: 'unchanged' | 'modified',
    hash?: string,
    isPrivate: boolean,
    stateId: string
  }> = {};

  Object.keys(currentState.public_data).forEach(key => {
    const currentValue = currentState.public_data[key];
    const previousValue = previousState?.public_data[key];
    const isModified = index > 0 && previousValue !== undefined && JSON.stringify(currentValue) !== JSON.stringify(previousValue);

    result[key] = {
      value: currentValue,
      status: isModified ? 'modified' : 'unchanged',
      hash: currentState.pcd_commitment[key],
      isPrivate: false,
      stateId: currentState.state_id
    };
  });

  if (index === 0 && currentPrivateData) {
    Object.entries(currentPrivateData).forEach(([key, value]) => {
      result[key] = {
        value,
        status: 'unchanged',
        hash: currentState.pcd_commitment[key],
        isPrivate: true,
        stateId: currentState.state_id
      };
    });
  } else if (previousPrivateData) {
    Object.entries(previousPrivateData).forEach(([key, value]) => {
      result[key] = {
        value,
        status: 'unchanged',
        hash: previousState?.pcd_commitment[key],
        isPrivate: true,
        stateId: previousState!.state_id
      };
    });
    if (currentPrivateData) {
      Object.entries(currentPrivateData).forEach(([key, value]) => {
        result[key] = {
          value,
          status: 'modified',
          hash: currentState.pcd_commitment[key],
          isPrivate: true,
          stateId: currentState.state_id
        };
      });
    }
  }

  return result;
};

function ProcessesViewer({ processes, myProcesses, onProcessesUpdate }: ProcessesViewerProps) {
  const [expandedBlocks, setExpandedBlocks] = useState<string[]>([]);
  const [isFiltered, setIsFiltered] = useState<boolean>(false);
  const [privateData, setPrivateData] = useState<Record<string, Record<string, any>>>({});
  const [editingField, setEditingField] = useState<{processId: string; stateId: string; key: string; value: any;} | null>(null);
  const [tempValue, setTempValue] = useState<any>(null);

  const toggleBlock = (blockId: string) => {
    setExpandedBlocks(prev => prev.includes(blockId) ? prev.filter(id => id !== blockId) : [...prev, blockId]);
  };

  const handleFilterClick = () => setIsFiltered(prev => !prev);

  if (!processes || Object.keys(processes).length === 0) {
    return (
      <div className="flex flex-col items-center justify-center py-12 text-gray-500">
        <h3 className="text-lg font-medium mb-2">Aucun processus disponible</h3>
        <p>Connectez-vous pour voir vos processus</p>
      </div>
    );
  }

  const fetchPrivateData = async (processId: string, stateId: string) => {
    if (!expandedBlocks.includes(processId) || !myProcesses.includes(processId)) return;
    try {
      const messageBus = MessageBus.getInstance(iframeUrl);
      await messageBus.isReady();
      const data = await messageBus.getData(processId, stateId);
      setPrivateData(prev => ({ ...prev, [stateId]: data }));
    } catch (err) {
      console.error(err);
    }
  };

  const handleDownload = (name: string | undefined, fileBlob: FileBlob) => {
    const blob = new Blob([fileBlob.data], { type: fileBlob.type });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = name || 'download';
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const formatValue = (key: string, value: string | number[] | FileBlob) => {
    if (isFileBlob(value)) {
      return (
        <button className="text-blue-600 hover:underline" onClick={() => handleDownload(key, value)}>
          📥 Télécharger
        </button>
      );
    }
    return <span>{JSON.stringify(value || '')}</span>;
  };

  const getDataIcon = (value: any) => {
    if (isFileBlob(value)) return '📄';
    if (typeof value === 'string') return '📝';
    if (typeof value === 'number') return '🔢';
    if (Array.isArray(value)) return '📋';
    if (typeof value === 'boolean') return '✅';
    return '📦';
  };

  const handleFieldUpdate = async (processId: string, stateId: string, key: string, value: any) => {
    try {
      const messageBus = MessageBus.getInstance(iframeUrl);
      await messageBus.isReady();
      const updatedProcess = await messageBus.updateProcess(processId, stateId, { [key]: value }, [], null);
      if (!updatedProcess) throw new Error('No updated process found');
      const newStateId = updatedProcess.diffs[0]?.state_id;
      if (!newStateId) throw new Error('No new state id found');
      await messageBus.notifyProcessUpdate(processId, newStateId);
      await messageBus.validateState(processId, newStateId);
      const updatedProcesses = await messageBus.getProcesses();
      onProcessesUpdate?.(updatedProcesses);
    } catch (err) {
      console.error(err);
    }
  };

  const renderEditForm = (key: string, value: any, onSave: (v: any) => void, onCancel: () => void) => {
    if (tempValue === null) setTempValue(value);

    const handleFormClick = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); };

    if (isFileBlob(value)) {
      return (
        <div className="flex flex-col space-y-2" onClick={handleFormClick}>
          <input type="file" onChange={(e) => {
            e.stopPropagation();
            const file = e.target.files?.[0];
            if (file) {
              const reader = new FileReader();
              reader.onload = (event) => {
                if (event.target?.result) {
                  setTempValue({ type: file.type, data: new Uint8Array(event.target.result as ArrayBuffer) });
                }
              };
              reader.readAsArrayBuffer(file);
            }
          }}/>
          <div className="flex space-x-2">
            <button className="px-2 py-1 bg-blue-500 text-white rounded" onClick={() => { onSave(tempValue); setTempValue(null); }}>Sauvegarder</button>
            <button className="px-2 py-1 bg-gray-300 rounded" onClick={() => { onCancel(); setTempValue(null); }}>Annuler</button>
          </div>
        </div>
      );
    }

    if (typeof value === 'boolean') {
      return (
        <div className="flex items-center space-x-2" onClick={handleFormClick}>
          <select className="border rounded px-2 py-1" value={tempValue.toString()} onChange={(e) => setTempValue(e.target.value === 'true')}>
            <option value="true">Vrai</option>
            <option value="false">Faux</option>
          </select>
          <div className="flex space-x-2">
            <button className="px-2 py-1 bg-blue-500 text-white rounded" onClick={() => onSave(tempValue)}>Sauvegarder</button>
            <button className="px-2 py-1 bg-gray-300 rounded" onClick={() => onCancel()}>Annuler</button>
          </div>
        </div>
      );
    }

    if (Array.isArray(value)) {
      return (
        <div className="flex flex-col space-y-2" onClick={handleFormClick}>
          <textarea className="border rounded p-2" rows={4} value={JSON.stringify(tempValue, null, 2)} onChange={(e) => {
            try { const parsed = JSON.parse(e.target.value); if (Array.isArray(parsed)) setTempValue(parsed); } catch {}
          }}/>
          <div className="flex space-x-2">
            <button className="px-2 py-1 bg-blue-500 text-white rounded" onClick={() => onSave(tempValue)}>Sauvegarder</button>
            <button className="px-2 py-1 bg-gray-300 rounded" onClick={() => onCancel()}>Annuler</button>
          </div>
        </div>
      );
    }

    return (
      <div className="flex space-x-2 items-center" onClick={handleFormClick}>
        <input className="border rounded px-2 py-1" type={typeof value === 'number' ? 'number' : 'text'} value={tempValue} onChange={(e) => setTempValue(typeof value === 'number' ? parseFloat(e.target.value) : e.target.value)} autoFocus />
        <div className="flex space-x-2">
          <button className="px-2 py-1 bg-blue-500 text-white rounded" onClick={() => onSave(tempValue)}>Sauvegarder</button>
          <button className="px-2 py-1 bg-gray-300 rounded" onClick={() => onCancel()}>Annuler</button>
        </div>
      </div>
    );
  };

  const renderDataField = (key: string, value: any, hash: string | undefined, isPrivate: boolean, processId: string, stateId: string, status: 'unchanged' | 'modified' = 'unchanged', originStateId?: string) => {
    const isEditing = editingField?.key === key && editingField?.processId === processId && editingField?.stateId === stateId;
    return (
      <div key={key} className={`border rounded p-2 mb-2 transition-colors ${status === 'modified' ? 'bg-green-100' : 'bg-white'}`}>
        <div className="flex items-center justify-between mb-1">
          <div className="flex items-center space-x-1">
            <span title={isPrivate ? 'Donnée privée' : 'Donnée publique'}>{isPrivate ? '🔒' : '🌐'}</span>
            <span>{getDataIcon(value)}</span>
            <span className="font-medium">{key}</span>
            {originStateId && originStateId !== stateId && <span title={`Propagé depuis l'état ${originStateId}`}>↺</span>}
          </div>
          <button className="text-sm text-gray-500 hover:text-gray-700" onClick={(e) => { e.stopPropagation(); setEditingField({ processId, stateId, key, value }); }}>
            {isEditing ? '✕' : '🔄'}
          </button>
        </div>
        <div>
          {isEditing ? renderEditForm(key, value, async (v) => { await handleFieldUpdate(processId, stateId, key, v); setEditingField(null); setTempValue(null); }, () => { setEditingField(null); setTempValue(null); }) : (
            <div className="flex items-center space-x-1">
              <span>{formatValue(key, value)}</span>
              {hash && <span title={`Hash: ${hash}`}>🔑</span>}
            </div>
          )}
        </div>
      </div>
    );
  };

  return (
    <div className="w-full h-full overflow-auto p-2">
      <div className="flex justify-between items-center mb-4">
        <h2 className="text-xl font-semibold">Processus</h2>
        <button className="px-2 py-1 border rounded text-sm" onClick={handleFilterClick}>{isFiltered ? 'Show All' : 'Filter'}</button>
      </div>
      <p className="mb-2 text-sm text-gray-500">{isFiltered ? Object.keys(processes).filter(p => myProcesses.includes(p)).length : Object.keys(processes).length} processus disponible(s)</p>
      <div className="space-y-4">
        {Object.entries(processes).map(([processId, process]) => {
          if (isFiltered && !myProcesses.includes(processId)) return null;
          const isExpanded = expandedBlocks.includes(processId);
          const stateCount = process.states.length - 1;

          return (
            <div key={processId} className="border rounded shadow-sm">
              <div className="flex justify-between items-center p-2 cursor-pointer bg-gray-50" onClick={() => toggleBlock(processId)}>
                <div className="font-mono">{processId.slice(0,8)}...{processId.slice(-4)}</div>
                <div>{stateCount} état(s)</div>
                <div>{isExpanded ? '▼' : '▶'}</div>
              </div>

              {isExpanded && (
                <div className="p-2 space-y-2 bg-white">
                  <div><strong>Process ID:</strong> {processId}</div>
                  {process.states.map((state, index) => {
                    if (index === stateCount) return null;
                    if (myProcesses.includes(processId) && !privateData[state.state_id]) setTimeout(() => fetchPrivateData(processId, state.state_id), 0);
                    const statePrivateData = privateData[state.state_id] || {};
                    const stateData = compareStates(state, index, index > 0 ? process.states[index-1] : undefined, statePrivateData, index > 0 ? privateData[process.states[index-1].state_id] : undefined);

                    return (
                      <div key={`${processId}-state-${index}`} className="border-t pt-2">
                        <h4 className="font-medium mb-1">État {index+1}</h4>
                        <div className="text-sm mb-1"><strong>TransactionId:</strong> {state.commited_in}</div>
                        <div className="text-sm mb-2"><strong>Empreinte totale de l'état:</strong> {state.state_id}</div>
                        <div className="space-y-1">
                          {Object.entries(stateData).map(([key, { value, status, hash, isPrivate, stateId }]) => renderDataField(key, value, hash, isPrivate, processId, stateId, status, state.state_id))}
                          {myProcesses.includes(processId) && Object.keys(statePrivateData).length === 0 && <div className="text-gray-400 text-sm">Chargement des données privées...</div>}
                          {!myProcesses.includes(processId) && <div className="text-gray-400 text-sm">🔒 Vous n'avez pas accès aux données privées de ce processus</div>}
                        </div>
                      </div>
                    );
                  })}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}

ProcessesViewer.displayName = 'ProcessesViewer';
export default memo(ProcessesViewer);
