/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

import * as React from 'react';
import StyleSheet from '../../StyleSheet/StyleSheet';
import Platform from '../../Utilities/Platform';
import Text from '../../Text/Text';
import View from '../../Components/View/View';
import LogBoxButton from './LogBoxButton';
import * as LogBoxStyle from './LogBoxStyle';
import LogBoxInspectorSection from './LogBoxInspectorSection';
import openFileInEditor from '../../Core/Devtools/openFileInEditor';
import type LogBoxLog from '../Data/LogBoxLog';

type Props = $ReadOnly<{|
  log: LogBoxLog,
|}>;

const BEFORE_SLASH_RE = /^(.*)[\\/]/;

// Taken from React https://github.com/facebook/react/blob/206d61f72214e8ae5b935f0bf8628491cb7f0797/packages/react-devtools-shared/src/backend/describeComponentFrame.js#L27-L41
function getPrettyFileName(path) {
  let fileName = path.replace(BEFORE_SLASH_RE, '');

  // In DEV, include code for a common special case:
  // prefer "folder/index.js" instead of just "index.js".
  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, '');
        // Note the below string contains a zero width space after the "/" character.
        // This is to prevent browsers like Chrome from formatting the file name as a link.
        // (Since this is a source link, it would not work to open the source file anyway.)
        fileName = folderName + '/​' + fileName;
      }
    }
  }

  return fileName;
}
function LogBoxInspectorReactFrames(props: Props): React.Node {
  const [collapsed, setCollapsed] = React.useState(true);
  if (props.log.componentStack == null || props.log.componentStack.length < 1) {
    return null;
  }

  function getStackList() {
    if (collapsed) {
      return props.log.componentStack.slice(0, 3);
    } else {
      return props.log.componentStack;
    }
  }

  function getCollapseMessage() {
    if (props.log.componentStack.length <= 3) {
      return;
    }

    const count = props.log.componentStack.length - 3;
    if (collapsed) {
      return `See ${count} more components`;
    } else {
      return `Collapse ${count} components`;
    }
  }

  return (
    <LogBoxInspectorSection heading="Component Stack">
      {getStackList().map((frame, index) => (
        <View
          // Unfortunately we don't have a unique identifier for stack traces.
          key={index}
          style={componentStyles.frameContainer}>
          <LogBoxButton
            backgroundColor={{
              default: 'transparent',
              pressed: LogBoxStyle.getBackgroundColor(1),
            }}
            onPress={
              // Older versions of DevTools do not provide full path.
              // This will not work on Windows, remove check once the
              // DevTools return the full file path.
              frame.fileName.startsWith('/')
                ? () =>
                    openFileInEditor(frame.fileName, frame.location?.row ?? 1)
                : null
            }
            style={componentStyles.frame}>
            <View style={componentStyles.component}>
              <Text style={componentStyles.frameName}>
                <Text style={componentStyles.bracket}>{'<'}</Text>
                {frame.content}
                <Text style={componentStyles.bracket}>{' />'}</Text>
              </Text>
            </View>
            <Text style={componentStyles.frameLocation}>
              {getPrettyFileName(frame.fileName)}
              {frame.location ? `:${frame.location.row}` : ''}
            </Text>
          </LogBoxButton>
        </View>
      ))}
      <View style={componentStyles.collapseContainer}>
        <LogBoxButton
          backgroundColor={{
            default: 'transparent',
            pressed: LogBoxStyle.getBackgroundColor(1),
          }}
          onPress={() => setCollapsed(!collapsed)}
          style={componentStyles.collapseButton}>
          <Text style={componentStyles.collapse}>{getCollapseMessage()}</Text>
        </LogBoxButton>
      </View>
    </LogBoxInspectorSection>
  );
}

const componentStyles = StyleSheet.create({
  collapseContainer: {
    marginLeft: 15,
    flexDirection: 'row',
  },
  collapseButton: {
    borderRadius: 5,
  },
  collapse: {
    color: LogBoxStyle.getTextColor(0.7),
    fontSize: 12,
    fontWeight: '300',
    lineHeight: 20,
    marginTop: 0,
    paddingVertical: 5,
    paddingHorizontal: 10,
  },
  frameContainer: {
    flexDirection: 'row',
    paddingHorizontal: 15,
  },
  frame: {
    flex: 1,
    paddingVertical: 4,
    paddingHorizontal: 10,
    borderRadius: 5,
  },
  component: {
    flexDirection: 'row',
    paddingRight: 10,
  },
  frameName: {
    fontFamily: Platform.select({android: 'monospace', ios: 'Menlo'}),
    color: LogBoxStyle.getTextColor(1),
    fontSize: 14,
    includeFontPadding: false,
    lineHeight: 18,
  },
  bracket: {
    fontFamily: Platform.select({android: 'monospace', ios: 'Menlo'}),
    color: LogBoxStyle.getTextColor(0.4),
    fontSize: 14,
    fontWeight: '500',
    includeFontPadding: false,
    lineHeight: 18,
  },
  frameLocation: {
    color: LogBoxStyle.getTextColor(0.7),
    fontSize: 12,
    fontWeight: '300',
    includeFontPadding: false,
    lineHeight: 16,
    paddingLeft: 10,
  },
});

export default LogBoxInspectorReactFrames;
