/**
 * useFrappeFieldBehavior Hook
 * 
 * Integrates with existing forms to provide Frappe's dynamic field behavior:
 * - depends_on (conditional visibility)
 * - mandatory_depends_on (conditional mandatory)
 * - read_only_depends_on (conditional read-only)
 * - fetch_from (auto-fetch values)
 * 
 * Usage:
 * const { getFieldState, shouldShowField, isMandatory, isReadOnly, processFieldValue } = useFrappeFieldBehavior('Asset', doc);
 */

import { useState, useEffect, useCallback, useMemo } from 'react';
import apiService from '../services/apiService';
import { FieldConfig, evaluateFrappeExpression, parseFetchFrom } from '../utils/frappeExpressionEvaluator';

interface FieldBehaviorState {
  isVisible: boolean;
  isReadOnly: boolean;
  isMandatory: boolean;
}

interface UseFrappeFieldBehaviorResult {
  loading: boolean;
  error: string | null;
  fields: FieldConfig[];
  getFieldState: (fieldname: string) => FieldBehaviorState;
  shouldShowField: (fieldname: string) => boolean;
  isMandatory: (fieldname: string) => boolean;
  isReadOnly: (fieldname: string) => boolean;
  getFieldLabel: (fieldname: string) => string;
  getFieldOptions: (fieldname: string) => string[];
  getFetchFromValue: (fieldname: string, linkedDoc: Record<string, any> | null) => any;
  validateMandatory: () => { valid: boolean; errors: Record<string, string> };
}

// Cache for doctype fields
const fieldCache: Record<string, FieldConfig[]> = {};

export function useFrappeFieldBehavior(
  doctype: string,
  doc: Record<string, any>
): UseFrappeFieldBehaviorResult {
  const [fields, setFields] = useState<FieldConfig[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  // Fetch doctype field configuration
  useEffect(() => {
    if (!doctype) {
      setLoading(false);
      return;
    }

    // Check cache
    if (fieldCache[doctype]) {
      setFields(fieldCache[doctype]);
      setLoading(false);
      return;
    }

    const fetchFields = async () => {
      setLoading(true);
      try {
        // Try to fetch from DocType
        const response = await apiService.apiCall<any>(
          `/api/method/frappe.client.get_doc?doctype=DocType&name=${encodeURIComponent(doctype)}`,
          { credentials: 'include' }
        );

        if (response?.message?.fields) {
          const fieldConfigs: FieldConfig[] = response.message.fields.map((f: any) => ({
            fieldname: f.fieldname,
            label: f.label,
            fieldtype: f.fieldtype,
            options: f.options,
            reqd: f.reqd,
            hidden: f.hidden,
            read_only: f.read_only,
            depends_on: f.depends_on,
            mandatory_depends_on: f.mandatory_depends_on,
            read_only_depends_on: f.read_only_depends_on,
            fetch_from: f.fetch_from,
            fetch_if_empty: f.fetch_if_empty,
            default: f.default,
            description: f.description,
            in_list_view: f.in_list_view,
            permlevel: f.permlevel,
            allow_on_submit: f.allow_on_submit,
          }));

          fieldCache[doctype] = fieldConfigs;
          setFields(fieldConfigs);
        }
      } catch (err: any) {
        console.warn(`Could not fetch DocType meta for ${doctype}:`, err.message);
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchFields();
  }, [doctype]);

  // Create a map for quick field lookup
  const fieldMap = useMemo(() => {
    const map: Record<string, FieldConfig> = {};
    fields.forEach(f => {
      map[f.fieldname] = f;
    });
    return map;
  }, [fields]);

  // Get complete field state
  const getFieldState = useCallback((fieldname: string): FieldBehaviorState => {
    const config = fieldMap[fieldname];
    
    if (!config) {
      // Field not found in config - return defaults
      return { isVisible: true, isReadOnly: false, isMandatory: false };
    }

    // Base visibility
    let isVisible = !(config.hidden === 1 || config.hidden === true);
    
    // Evaluate depends_on
    if (config.depends_on && isVisible) {
      isVisible = evaluateFrappeExpression(config.depends_on, doc);
    }

    // Base read-only
    let isReadOnly = config.read_only === 1 || config.read_only === true;
    
    // Evaluate read_only_depends_on
    if (config.read_only_depends_on) {
      isReadOnly = isReadOnly || evaluateFrappeExpression(config.read_only_depends_on, doc);
    }

    // Base mandatory
    let isMandatory = config.reqd === 1 || config.reqd === true;
    
    // Evaluate mandatory_depends_on
    if (config.mandatory_depends_on) {
      isMandatory = isMandatory || evaluateFrappeExpression(config.mandatory_depends_on, doc);
    }

    return { isVisible, isReadOnly, isMandatory };
  }, [fieldMap, doc]);

  // Convenience methods
  const shouldShowField = useCallback((fieldname: string): boolean => {
    return getFieldState(fieldname).isVisible;
  }, [getFieldState]);

  const isMandatory = useCallback((fieldname: string): boolean => {
    const state = getFieldState(fieldname);
    return state.isVisible && state.isMandatory;
  }, [getFieldState]);

  const isReadOnly = useCallback((fieldname: string): boolean => {
    return getFieldState(fieldname).isReadOnly;
  }, [getFieldState]);

  const getFieldLabel = useCallback((fieldname: string): string => {
    const config = fieldMap[fieldname];
    return config?.label || fieldname;
  }, [fieldMap]);

  const getFieldOptions = useCallback((fieldname: string): string[] => {
    const config = fieldMap[fieldname];
    if (!config?.options) return [];
    
    if (config.fieldtype === 'Select') {
      return config.options.split('\n').filter(opt => opt.trim() !== '');
    }
    
    return [];
  }, [fieldMap]);

  // Get value from linked document based on fetch_from
  const getFetchFromValue = useCallback((fieldname: string, linkedDoc: Record<string, any> | null): any => {
    const config = fieldMap[fieldname];
    if (!config?.fetch_from || !linkedDoc) return undefined;

    const parsed = parseFetchFrom(config.fetch_from);
    if (!parsed) return undefined;

    // The linkedDoc should have the target field
    return linkedDoc[parsed.targetField];
  }, [fieldMap]);

  // Validate all mandatory fields
  const validateMandatory = useCallback((): { valid: boolean; errors: Record<string, string> } => {
    const errors: Record<string, string> = {};

    fields.forEach(field => {
      const state = getFieldState(field.fieldname);
      
      if (state.isVisible && state.isMandatory) {
        const value = doc[field.fieldname];
        if (value === undefined || value === null || value === '') {
          errors[field.fieldname] = `${field.label || field.fieldname} is required`;
        }
      }
    });

    return {
      valid: Object.keys(errors).length === 0,
      errors
    };
  }, [fields, doc, getFieldState]);

  return {
    loading,
    error,
    fields,
    getFieldState,
    shouldShowField,
    isMandatory,
    isReadOnly,
    getFieldLabel,
    getFieldOptions,
    getFetchFromValue,
    validateMandatory
  };
}

export default useFrappeFieldBehavior;

