import type {Source} from 'shared/ReactElementType';
import type {LazyComponent} from 'react/src/ReactLazy';
import {enableComponentStackLocations} from 'shared/ReactFeatureFlags';
import {
  REACT_SUSPENSE_TYPE,
  REACT_SUSPENSE_LIST_TYPE,
  REACT_FORWARD_REF_TYPE,
  REACT_MEMO_TYPE,
  REACT_LAZY_TYPE,
} from 'shared/ReactSymbols';
import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
import ReactSharedInternals from 'shared/ReactSharedInternals';
const {ReactCurrentDispatcher} = ReactSharedInternals;
let prefix;
export function describeBuiltInComponentFrame(
  name: string,
  source: void | null | Source,
  ownerFn: void | null | Function,
): string {
  if (enableComponentStackLocations) {
    if (prefix === undefined) {
      
      try {
        throw Error();
      } catch (x) {
        const match = x.stack.trim().match(/\n( *(at )?)/);
        prefix = (match && match[1]) || '';
      }
    }
    
    return '\n' + prefix + name;
  } else {
    let ownerName = null;
    if (__DEV__ && ownerFn) {
      ownerName = ownerFn.displayName || ownerFn.name || null;
    }
    return describeComponentFrame(name, source, ownerName);
  }
}
let reentry = false;
let componentFrameCache;
if (__DEV__) {
  const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
  componentFrameCache = new PossiblyWeakMap<Function, string>();
}
export function describeNativeComponentFrame(
  fn: Function,
  construct: boolean,
): string {
  
  if (!fn || reentry) {
    return '';
  }
  if (__DEV__) {
    const frame = componentFrameCache.get(fn);
    if (frame !== undefined) {
      return frame;
    }
  }
  let control;
  reentry = true;
  const previousPrepareStackTrace = Error.prepareStackTrace;
  
  Error.prepareStackTrace = undefined;
  let previousDispatcher;
  if (__DEV__) {
    previousDispatcher = ReactCurrentDispatcher.current;
    
    
    ReactCurrentDispatcher.current = null;
    disableLogs();
  }
  try {
    
    if (construct) {
      
      const Fake = function () {
        throw Error();
      };
      
      Object.defineProperty(Fake.prototype, 'props', {
        set: function () {
          
          
          throw Error();
        },
      });
      if (typeof Reflect === 'object' && Reflect.construct) {
        
        
        try {
          Reflect.construct(Fake, []);
        } catch (x) {
          control = x;
        }
        Reflect.construct(fn, [], Fake);
      } else {
        try {
          Fake.call();
        } catch (x) {
          control = x;
        }
        
        fn.call(Fake.prototype);
      }
    } else {
      try {
        throw Error();
      } catch (x) {
        control = x;
      }
      
      
      
      fn();
    }
  } catch (sample) {
    
    if (sample && control && typeof sample.stack === 'string') {
      
      
      const sampleLines = sample.stack.split('\n');
      const controlLines = control.stack.split('\n');
      let s = sampleLines.length - 1;
      let c = controlLines.length - 1;
      while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) {
        
        
        
        
        
        
        c--;
      }
      for (; s >= 1 && c >= 0; s--, c--) {
        
        
        if (sampleLines[s] !== controlLines[c]) {
          
          
          
          
          
          if (s !== 1 || c !== 1) {
            do {
              s--;
              c--;
              
              
              if (c < 0 || sampleLines[s] !== controlLines[c]) {
                
                let frame = '\n' + sampleLines[s].replace(' at new ', ' at ');
                
                
                
                if (fn.displayName && frame.includes('<anonymous>')) {
                  frame = frame.replace('<anonymous>', fn.displayName);
                }
                if (__DEV__) {
                  if (typeof fn === 'function') {
                    componentFrameCache.set(fn, frame);
                  }
                }
                
                return frame;
              }
            } while (s >= 1 && c >= 0);
          }
          break;
        }
      }
    }
  } finally {
    reentry = false;
    if (__DEV__) {
      ReactCurrentDispatcher.current = previousDispatcher;
      reenableLogs();
    }
    Error.prepareStackTrace = previousPrepareStackTrace;
  }
  
  const name = fn ? fn.displayName || fn.name : '';
  const syntheticFrame = name ? describeBuiltInComponentFrame(name) : '';
  if (__DEV__) {
    if (typeof fn === 'function') {
      componentFrameCache.set(fn, syntheticFrame);
    }
  }
  return syntheticFrame;
}
const BEFORE_SLASH_RE = /^(.*)[\\\/]/;
function describeComponentFrame(
  name: null | string,
  source: void | null | Source,
  ownerName: null | string,
) {
  let sourceInfo = '';
  if (__DEV__ && source) {
    const path = source.fileName;
    let fileName = path.replace(BEFORE_SLASH_RE, '');
    
    
    if (/^index\./.test(fileName)) {
      const match = path.match(BEFORE_SLASH_RE);
      if (match) {
        const pathBeforeSlash = match[1];
        if (pathBeforeSlash) {
          const folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
          fileName = folderName + '/' + fileName;
        }
      }
    }
    sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
  } else if (ownerName) {
    sourceInfo = ' (created by ' + ownerName + ')';
  }
  return '\n    in ' + (name || 'Unknown') + sourceInfo;
}
export function describeClassComponentFrame(
  ctor: Function,
  source: void | null | Source,
  ownerFn: void | null | Function,
): string {
  if (enableComponentStackLocations) {
    return describeNativeComponentFrame(ctor, true);
  } else {
    return describeFunctionComponentFrame(ctor, source, ownerFn);
  }
}
export function describeFunctionComponentFrame(
  fn: Function,
  source: void | null | Source,
  ownerFn: void | null | Function,
): string {
  if (enableComponentStackLocations) {
    return describeNativeComponentFrame(fn, false);
  } else {
    if (!fn) {
      return '';
    }
    const name = fn.displayName || fn.name || null;
    let ownerName = null;
    if (__DEV__ && ownerFn) {
      ownerName = ownerFn.displayName || ownerFn.name || null;
    }
    return describeComponentFrame(name, source, ownerName);
  }
}
function shouldConstruct(Component: Function) {
  const prototype = Component.prototype;
  return !!(prototype && prototype.isReactComponent);
}
export function describeUnknownElementTypeFrameInDEV(
  type: any,
  source: void | null | Source,
  ownerFn: void | null | Function,
): string {
  if (!__DEV__) {
    return '';
  }
  if (type == null) {
    return '';
  }
  if (typeof type === 'function') {
    if (enableComponentStackLocations) {
      return describeNativeComponentFrame(type, shouldConstruct(type));
    } else {
      return describeFunctionComponentFrame(type, source, ownerFn);
    }
  }
  if (typeof type === 'string') {
    return describeBuiltInComponentFrame(type, source, ownerFn);
  }
  switch (type) {
    case REACT_SUSPENSE_TYPE:
      return describeBuiltInComponentFrame('Suspense', source, ownerFn);
    case REACT_SUSPENSE_LIST_TYPE:
      return describeBuiltInComponentFrame('SuspenseList', source, ownerFn);
  }
  if (typeof type === 'object') {
    switch (type.$$typeof) {
      case REACT_FORWARD_REF_TYPE:
        return describeFunctionComponentFrame(type.render, source, ownerFn);
      case REACT_MEMO_TYPE:
        
        return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);
      case REACT_LAZY_TYPE: {
        const lazyComponent: LazyComponent<any, any> = (type: any);
        const payload = lazyComponent._payload;
        const init = lazyComponent._init;
        try {
          
          return describeUnknownElementTypeFrameInDEV(
            init(payload),
            source,
            ownerFn,
          );
        } catch (x) {}
      }
    }
  }
  return '';
}