// ===== Utility Functions for Validation & Storage =====

// ===== VALIDATION UTILITIES =====
const Validators = {
  // Validasi string tidak kosong
  required: (value, fieldName) => {
    if (!value?.toString().trim()) {
      return `${fieldName} wajib diisi`;
    }
    return null;
  },

  // Validasi angka dalam rentang
  numberRange: (value, fieldName, min = 0, max = 999999) => {
    const num = Number(value);
    if (isNaN(num)) {
      return `${fieldName} harus berupa angka`;
    }
    if (num < min) {
      return `${fieldName} minimal ${min}`;
    }
    if (num > max) {
      return `${fieldName} maksimal ${max}`;
    }
    return null;
  },

  // Validasi tanggal masa depan
  futureDate: (value, fieldName) => {
    const date = new Date(value);
    if (isNaN(date.getTime())) {
      return `${fieldName} harus tanggal valid`;
    }
    if (date <= new Date()) {
      return `${fieldName} harus tanggal masa depan`;
    }
    return null;
  },

  // Validasi email
  email: (value, fieldName) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (value && !emailRegex.test(value)) {
      return `${fieldName} harus email valid`;
    }
    return null;
  },

  // Validasi panjang string
  length: (value, fieldName, minLen, maxLen) => {
    const len = value?.toString().length || 0;
    if (len < minLen) {
      return `${fieldName} minimal ${minLen} karakter`;
    }
    if (len > maxLen) {
      return `${fieldName} maksimal ${maxLen} karakter`;
    }
    return null;
  },

  // Validasi file sudah dipilih
  fileSelected: (files, fieldName) => {
    if (!files || files.length === 0) {
      return `${fieldName} wajib dipilih`;
    }
    return null;
  }
};

// ===== VALIDATION RUNNER =====
const validateForm = (formData, rules) => {
  const errors = {};
  Object.keys(rules).forEach(field => {
    const fieldRules = Array.isArray(rules[field]) ? rules[field] : [rules[field]];
    for (let rule of fieldRules) {
      const error = rule(formData[field]);
      if (error) {
        errors[field] = error;
        break;
      }
    }
  });
  return errors;
};

// ===== LOCALSTORAGE UTILITIES =====
const Storage = {
  // Simpan ke localStorage
  set: (key, value) => {
    try {
      localStorage.setItem(key, JSON.stringify(value));
      return true;
    } catch (e) {
      console.warn(`Storage set failed for ${key}:`, e);
      return false;
    }
  },

  // Ambil dari localStorage
  get: (key, defaultValue = null) => {
    try {
      const item = localStorage.getItem(key);
      return item ? JSON.parse(item) : defaultValue;
    } catch (e) {
      console.warn(`Storage get failed for ${key}:`, e);
      return defaultValue;
    }
  },

  // Hapus dari localStorage
  remove: (key) => {
    try {
      localStorage.removeItem(key);
      return true;
    } catch (e) {
      console.warn(`Storage remove failed for ${key}:`, e);
      return false;
    }
  },

  // Clear semua storage
  clear: () => {
    try {
      localStorage.clear();
      return true;
    } catch (e) {
      console.warn(`Storage clear failed:`, e);
      return false;
    }
  }
};

// ===== CUSTOM HOOKS =====
function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = React.useState(() => {
    return Storage.get(key, initialValue);
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      Storage.set(key, valueToStore);
    } catch (e) {
      console.warn(`useLocalStorage error for ${key}:`, e);
    }
  };

  return [storedValue, setValue];
}

// Custom hook untuk form validation
function useFormValidation(initialValues, onSubmit) {
  const [values, setValues] = React.useState(initialValues);
  const [errors, setErrors] = React.useState({});
  const [touched, setTouched] = React.useState({});
  const [isSubmitting, setIsSubmitting] = React.useState(false);

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setValues({
      ...values,
      [name]: type === "checkbox" ? checked : value
    });
    // Clear error saat user mulai edit
    if (errors[name]) {
      setErrors({ ...errors, [name]: null });
    }
  };

  const handleBlur = (e) => {
    const { name } = e.target;
    setTouched({ ...touched, [name]: true });
  };

  const handleSubmit = async (e, validationRules) => {
    e?.preventDefault();
    const newErrors = validateForm(values, validationRules);
    setErrors(newErrors);

    if (Object.keys(newErrors).length === 0) {
      setIsSubmitting(true);
      try {
        await onSubmit(values);
      } finally {
        setIsSubmitting(false);
      }
    } else {
      window.showToast("Periksa kembali form anda", "warn");
    }
  };

  const resetForm = () => {
    setValues(initialValues);
    setErrors({});
    setTouched({});
  };

  return {
    values,
    setValues,
    errors,
    touched,
    isSubmitting,
    handleChange,
    handleBlur,
    handleSubmit,
    resetForm,
    setFieldValue: (name, value) => setValues({ ...values, [name]: value }),
    setFieldError: (name, error) => setErrors({ ...errors, [name]: error })
  };
}

// ===== C1: AUDIT LOGGING UTILITY =====
const AuditLog = {
  log: (action, context = {}) => {
    const entry = {
      _id: Math.random().toString(36).substr(2, 9),
      timestamp: new Date().toISOString(),
      user: sessionStorage.getItem('authedUser') || 'unknown',
      role: sessionStorage.getItem('authedRole') || 'unknown',
      action,
      context,
      userAgent: navigator.userAgent.substr(0, 100)
    };

    const logs = JSON.parse(localStorage.getItem('audit_log') || '[]');
    logs.push(entry);
    if (logs.length > 1000) logs.shift();
    localStorage.setItem('audit_log', JSON.stringify(logs));
  },

  getLogs: () => {
    return JSON.parse(localStorage.getItem('audit_log') || '[]');
  },

  exportCSV: () => {
    const logs = AuditLog.getLogs();
    const headers = ['Timestamp', 'User', 'Role', 'Action', 'Context', 'UserAgent'];
    const rows = logs.map(l => [
      l.timestamp,
      l.user,
      l.role,
      l.action,
      JSON.stringify(l.context).substr(0, 100),
      l.userAgent
    ]);

    const csv = [headers, ...rows]
      .map(r => r.map(v => `"${v}"`).join(','))
      .join('\n');

    const blob = new Blob([csv], { type: 'text/csv' });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `IDSS-AuditLog-${new Date().toISOString().split('T')[0]}.csv`;
    a.click();
  }
};

// ===== C2: MESSAGE BUS WITH VERSIONING =====
const MessageBus = {
  seenMessages: {},

  getMessageType: (typeObj) => {
    return typeof typeObj === 'string' ? typeObj : typeObj.type || typeObj;
  },

  send: (typeObj, payload) => {
    if (!typeObj || !payload) {
      console.error('MessageBus.send requires typeObj and payload');
      return;
    }

    const messageType = MessageBus.getMessageType(typeObj);
    const metadata = window.IDSS?.MESSAGE_METADATA?.[messageType] || { version: 1, requiresAck: false };

    const idempotencyKey = payload._idempotencyKey ||
      `${messageType}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

    if (MessageBus.seenMessages[idempotencyKey]) {
      console.log(`[MessageBus] Duplicate ignored: ${messageType}`);
      return;
    }

    MessageBus.seenMessages[idempotencyKey] = true;

    window.postMessage({
      type: messageType,
      _version: metadata.version || 1,
      _messageId: Math.random().toString(36).substr(2, 9),
      _idempotencyKey: idempotencyKey,
      _timestamp: new Date().toISOString(),
      payload
    }, '*');

    if (window.AuditLog) {
      window.AuditLog.log('message_sent', { type: messageType, idempotencyKey });
    }
  },

  clearSeenMessages: () => {
    MessageBus.seenMessages = {};
  }
};

// ===== C3: OFFLINE SYNC QUEUE (CRITICAL FOR FIELD DEPLOYMENT) =====
const OfflineQueue = {
  queue: [],
  isOnline: typeof navigator !== 'undefined' ? navigator.onLine : true,

  init: () => {
    OfflineQueue.queue = JSON.parse(localStorage.getItem('offline_queue') || '[]');

    window.addEventListener('online', OfflineQueue.onOnline);
    window.addEventListener('offline', OfflineQueue.onOffline);
  },

  add: (type, payload) => {
    const entry = {
      type,
      payload,
      _queuedAt: new Date().toISOString(),
      _retries: 0,
      _id: `q-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
    };

    OfflineQueue.queue.push(entry);
    localStorage.setItem('offline_queue', JSON.stringify(OfflineQueue.queue));

    if (window.showToast) {
      window.showToast('Mode Offline: Pengajuan akan dikirim saat terhubung', 'warn', 3000);
    }

    if (window.AuditLog) {
      window.AuditLog.log('message_queued_offline', { type, queueSize: OfflineQueue.queue.length });
    }
  },

  replay: async () => {
    if (!OfflineQueue.isOnline || OfflineQueue.queue.length === 0) return;

    if (window.showToast) {
      window.showToast('Online. Mengirim pengajuan yang tertunda...', 'success', 2000);
    }

    const queue = [...OfflineQueue.queue];
    for (const entry of queue) {
      try {
        if (window.MessageBus) {
          window.MessageBus.send(entry.type, entry.payload);
        } else {
          window.postMessage({ type: entry.type, payload: entry.payload }, '*');
        }

        OfflineQueue.queue = OfflineQueue.queue.filter(e => e._id !== entry._id);

        if (window.AuditLog) {
          window.AuditLog.log('message_replayed_online', { type: entry.type, queueSize: OfflineQueue.queue.length });
        }
      } catch (e) {
        console.error('[OfflineQueue] Replay failed:', e);
        if (entry._retries < 3) {
          entry._retries++;
        } else {
          OfflineQueue.queue = OfflineQueue.queue.filter(e => e._id !== entry._id);
          if (window.AuditLog) {
            window.AuditLog.log('message_replay_failed_max_retries', { type: entry.type });
          }
        }
      }
    }

    localStorage.setItem('offline_queue', JSON.stringify(OfflineQueue.queue));
  },

  onOnline: () => {
    OfflineQueue.isOnline = true;
    OfflineQueue.replay();
  },

  onOffline: () => {
    OfflineQueue.isOnline = false;
    if (window.showToast) {
      window.showToast('Offline: Pengajuan akan disimpan dan dikirim saat terhubung', 'info', 3000);
    }
  },

  getStatus: () => ({
    isOnline: OfflineQueue.isOnline,
    queueLength: OfflineQueue.queue.length,
    oldestMessage: OfflineQueue.queue[0]?._queuedAt || null
  })
};

OfflineQueue.init();

// ===== C4: SLA TRACKING =====
const SLATracker = {
  calculateOverdue: (deadline) => {
    const now = new Date();
    const dl = new Date(deadline);
    if (now <= dl) return null;
    return Math.ceil((now - dl) / (1000 * 60 * 60 * 24));
  },

  checkRequestSLA: (request) => {
    const daysOverdue = SLATracker.calculateOverdue(request.deadline);
    return {
      isOverdue: daysOverdue !== null && daysOverdue > 0,
      daysLate: daysOverdue || 0,
      severity: daysOverdue > 7 ? 'critical' : daysOverdue > 0 ? 'warning' : 'ok'
    };
  },

  getOverdueRequests: (requests) => {
    return (requests || []).filter(r => SLATracker.checkRequestSLA(r).isOverdue);
  },

  calculateSLAMetrics: (requests) => {
    const total = (requests || []).length;
    const overdue = SLATracker.getOverdueRequests(requests);
    const onTimePercent = total > 0 ? ((total - overdue.length) / total * 100).toFixed(1) : 0;
    return { total, overdue: overdue.length, onTimePercent };
  }
};

// ===== C5: CROSS-TAB SYNC =====
const TabSync = {
  channel: typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel('idss-mbg') : null,

  broadcast: (message) => {
    if (TabSync.channel) {
      TabSync.channel.postMessage({
        type: 'STATE_UPDATE',
        payload: message,
        timestamp: new Date().toISOString()
      });
    }
  },

  listen: (callback) => {
    if (TabSync.channel) {
      TabSync.channel.onmessage = (event) => {
        if (event.data.type === 'STATE_UPDATE') {
          callback(event.data.payload);
        }
      };
    }
  }
};

// ===== C6: COMPLIANCE EXPORT =====
const ComplianceExport = {
  createHeader: () => [
    ['AUDIT COMPLIANCE EXPORT'],
    ['Exported By', sessionStorage.getItem('authedUser') || 'system'],
    ['Role', sessionStorage.getItem('authedRole') || 'unknown'],
    ['Timestamp', new Date().toISOString()],
    ['System', 'IDSS MBG v1.0 (Phase C)'],
    []
  ],

  downloadCSV: (filename, headers, rows) => {
    const footer = [
      [],
      ['Total Records', (rows || []).length.toString()],
      ['Exported', new Date().toISOString()]
    ];

    const allRows = [
      ...ComplianceExport.createHeader(),
      headers,
      ...(rows || []),
      ...footer
    ];

    const csv = allRows
      .map(r => r.map(v => `"${v}"`).join(','))
      .join('\n');

    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement('a');
    link.href = URL.createObjectURL(blob);
    link.download = filename;
    link.click();
  }
};

// Export semua
window.Validators = Validators;
window.validateForm = validateForm;
window.Storage = Storage;
window.useLocalStorage = useLocalStorage;
window.useFormValidation = useFormValidation;
window.AuditLog = AuditLog;
window.MessageBus = MessageBus;
window.OfflineQueue = OfflineQueue;
window.SLATracker = SLATracker;
window.TabSync = TabSync;
window.ComplianceExport = ComplianceExport;
