import React from "react";
import styles from "./DocsViewer.module.css";
import decoratorSomething from "./decoratorSomething";
import { Sandpack } from "@codesandbox/sandpack-react";
//import "@codesandbox/sandpack-react/dist/index.module.css";
import { CopyBlock, dracula } from "react-code-blocks";
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
import { renderToStaticMarkup } from "react-dom/server";
import reactElementToJSXString from "react-element-to-jsx-string";
import replaceAtLast from "../../utils/replaceAtLast";
import replaceAtFirst from "../../utils/replaceAtFirst";

const stringConstructor = "".constructor;
const arrayConstructor = [].constructor;
const objectConstructor = {}.constructor;

const propTypesList = {
  string: "string",
  boolean: "bool",
  object: "object",
  number: "number",
  array: "array",
  any: "any",
  node: "node",
  func: "func"
};

function whatIsIt(object, defaultType = propTypesList.any) {
  if (object === null) {
    return "null";
  }
  if (object === undefined) {
    return "undefined";
  }

  if (React.isValidElement(object)) {
    return propTypesList.node;
  }

  if (object.constructor === arrayConstructor) {
    return propTypesList.array;
  }
  if (object.constructor === objectConstructor) {
    return propTypesList.object;
  }

  if (typeof object === "function") {
    return propTypesList.func;
  }

  return defaultType;
}

const getConvertedPropValue = (
  propType,
  propertyValue,
  displayPropValue = "",
  propertyName
) => {
  let propertyType = whatIsIt(propertyValue, propType);

  switch (propertyType) {
    case propTypesList.string:
      displayPropValue = `"${propertyValue}"`;
      break;
    case propTypesList.object:
    case propTypesList.array:
      displayPropValue = `${JSON.stringify(propertyValue)}`;
      break;
    case propTypesList.node:
    case propTypesList.any:
      let componentName = upperCaseFirstLetter(propertyName);
      displayPropValue = `"<${componentName}PlaceHolder />"`;
      break;
    case propTypesList.func:
      displayPropValue = `"${propertyValue.name}Function()"`;
    default:
      displayPropValue = displayPropValue || `${propertyValue}`;
  }

  return displayPropValue;
};

const getDisplayValue = (
  propertyType = "",
  propertyValue,
  displayPropValue = "",
  propertyName
) => {
  if (propertyType.startsWith("union")) {
    let propTypes = propertyType.replace("union[", "").replace("]").split(",");
    propTypes.forEach((propType) => {
      displayPropValue = getConvertedPropValue(
        propType,
        propertyValue,
        displayPropValue,
        propertyName
      );
    });
  } else if (propertyType.startsWith("enum")) {
    displayPropValue = `"${propertyValue}"`;
  }

  return getConvertedPropValue(
    propertyType,
    propertyValue,
    displayPropValue,
    propertyName
  );
};

const invertObject = (obj = {}) => {
  return Object.fromEntries(Object.entries(obj).map((a) => a.reverse()));
};

function upperCaseFirstLetter(content) {
  return content.charAt(0).toUpperCase() + content.slice(1);
}

function CodeIcon() {
  return (
    <svg viewBox="0 0 122.88 91.26">
      <g>
        <path
          style="fill-rule:evenodd;clip-rule:evenodd;"
          d="M8.32,0h106.24c4.58,0,8.32,3.74,8.32,8.32v74.62c0,4.57-3.74,8.32-8.32,8.32H8.32C3.74,91.26,0,87.51,0,82.94 V8.32C0,3.74,3.74,0,8.32,0L8.32,0z M54.46,72.22L32,58.61v-8.63l22.46-13.61v10.26l-13.65,7.69l13.65,7.7V72.22L54.46,72.22z M68.42,72.22l22.46-13.61v-8.63L68.42,36.37v10.26l13.65,7.69l-13.65,7.7V72.22L68.42,72.22z M117.97,23.29H5.29v60.46 c0,0.64,0.25,1.2,0.67,1.63c0.42,0.42,0.99,0.67,1.63,0.67h108.04c0.64,0,1.2-0.25,1.63-0.67c0.43-0.43,0.67-0.99,0.67-1.63V23.29 H117.97L117.97,23.29z M106.64,9.35c2.27,0,4.11,1.84,4.11,4.11c0,2.27-1.84,4.11-4.11,4.11c-2.27,0-4.11-1.84-4.11-4.11 C102.54,11.19,104.38,9.35,106.64,9.35L106.64,9.35z M78.8,9.35c2.27,0,4.11,1.84,4.11,4.11c0,2.27-1.84,4.11-4.11,4.11 c-2.27,0-4.11-1.84-4.11-4.11C74.69,11.19,76.53,9.35,78.8,9.35L78.8,9.35z M92.72,9.35c2.27,0,4.11,1.84,4.11,4.11 c0,2.27-1.84,4.11-4.11,4.11c-2.27,0-4.11-1.84-4.11-4.11C88.61,11.19,90.45,9.35,92.72,9.35L92.72,9.35z"
        />
      </g>
    </svg>
  );
}

const excludeProperties = ["children"];

const generateCodeString = (component, properties, displayChildren) => {
  let componentName = component.name;
  let componentLC = componentName.toLowerCase();
  let componentPropTypes = component.propTypes || {};
  let componentEnumTypes = component.constantProps || {};

  let propsIntoString = "";
  Object.entries(properties).forEach((property) => {
    let propertyName = property[0];
    let propertyValue = property[1];

    if (excludeProperties.includes(propertyName)) {
      return;
    }

    let propertyType = componentPropTypes[propertyName]?.hookType;

    if (componentEnumTypes && componentEnumTypes[propertyName]) {
      let propertyKeyName = invertObject(componentEnumTypes[propertyName])[
        propertyValue
      ];
      propertyValue = `${componentLC}ConstantProps.${propertyName}.${propertyKeyName}`;
      propertyType = "";
    }

    let displayPropValue = "";
    displayPropValue = getDisplayValue(
      propertyType,
      propertyValue,
      displayPropValue,
      propertyName
    );

    propsIntoString += ` ${propertyName}={${displayPropValue}}`;
  });

  let componentCode = `\t<${componentName}${propsIntoString} />`;
  if (displayChildren) {
    let displayChildrenCode = reactElementToJSXString(displayChildren, {
      useBooleanShorthandSyntax: false,
      useFragmentShortSyntax: false,
      showDefaultProps: false,
      tabStop: 12
    });
    displayChildrenCode = replaceAtFirst(
      displayChildrenCode,
      "<React.Fragment>",
      ""
    );
    displayChildrenCode = replaceAtLast(
      displayChildrenCode,
      "</React.Fragment>",
      ""
    );
    //displayChildrenCode = replaceAtLast(displayChildrenCode,"<React.Fragment />","");

    componentCode = `\t<${componentName}${propsIntoString} >${displayChildrenCode}\t</${componentName}>`;
  }

  let componentImportCode = `\nimport ${componentName} from "@zohodesk/basecomponents/lib/components/${componentLC}/${componentName}";\nimport ${componentLC}ConstantProps from "@zohodesk/basecomponents/lib/components/${componentLC}/${componentName}.constantProps";
    `;

  return {
    componentImportCode,
    componentCode,
    componentType: component
  };
};

function CodePlayer({ initialCodes = [], isShowFullSource }) {
  let importCode = `import React from 'react';\nimport ThemeProvider from "@zohodesk/basecomponents/lib/components/themeprovider/ThemeProvider";`;

  let initialCode = "";
  let componentImportCode = "";
  initialCodes.map((codeItem) => {
    if (!componentImportCode) {
      componentImportCode = `${codeItem.componentImportCode} \n`;
    }

    if (!isShowFullSource) {
      initialCode += `${codeItem.componentCode} \n`;
    } else {
      initialCode += `${codeItem.componentCode} \n\n`;
    }
  });

  let fullSourceCode = `${importCode}`;
  fullSourceCode += `${componentImportCode}`;
  fullSourceCode += `export default function App() {
        return <ThemeProvider> \n\n ${initialCode} </ThemeProvider>
    }`;

  if (!isShowFullSource) {
    fullSourceCode = `\n${initialCode}`;
  }

  return (
    <div className={styles.code_player}>
      {/*<Sandpack
            template="react" 
            theme="dark" 

            options={{
                showTabs: false,
                closableTabs: false,
                wrapContent: true,
                showLineNumbers: false,
                showInlineErrors: true,
                classes: { 
                    "sp-layout" : styles.sp_layout,
                    "sp-stack" : styles.sp_stack
                }  
            }}
           
            customSetup={{
                dependencies: {
                    "@zohodesk/basecomponents" : "0.0.7",
                    "prop-types" : "latest"
                },
                files: {
                    "/App.js": initialCode
                }
         }} />

        <br /> */}

      {/*<Sandpack
            template="react" 
            theme="dark" 

            options={{
                showTabs: false,
                closableTabs: false,
                wrapContent: true,
                showLineNumbers: false,
                showInlineErrors: true,
                classes: { 
                    "sp-layout" : styles.sp_layout,
                    "sp-stack" : styles.sp_stack
                }  
            }}
           
            customSetup={{
                dependencies: {
                    "@zohodesk/basecomponents" : "latest",
                    "prop-types" : "latest"
                },
                files: {
                    "/App.js": showFullSourceCode
                }
            }} />*/}

      <CopyBlock
        text={fullSourceCode}
        language="jsx"
        showLineNumbers={false}
        wrapLongLines={true}
        theme={dracula}
        codeBlock
      />

      {/*<LiveProvider code={showFullSourceCode}>
                <LiveEditor disabled/>
    </LiveProvider>*/}
    </div>
  );
}

export default (props) => {
  
  let {
    $decoratorItemClassName: commonDecoratorItemClassName,
    $decorator : decorateList,
    $decoratorListClassName : decoratorListClassName,
    $decorateComponentItem : decorateCommonComponentItem
  } = props

  let initialCodes = [];
  let componentsList =
    props.componentsList &&
    props.componentsList.map((itemId, index) => {
      let componentDetail =
        props.componentsDetail && props.componentsDetail[itemId];
      if (!componentDetail) {
        return null;
      }

      let ComponentType = props.componentType;
      let { $decorator: decoratorItem, $decoratorClassName: decoratorClassName, children, ...originalComponentDetail } = componentDetail;


      let displayChildren = children;
      if (whatIsIt(children) === propTypesList.func) {
        displayChildren = children();
        if (whatIsIt(displayChildren) === propTypesList.array) {
          displayChildren = displayChildren.map((item,i)=>{
            return React.cloneElement(item,{key:i}); 
            })
        }

      }
      // else if (whatIsIt(children) === propTypesList.array) {
      //   displayChildren = children.map((item,i)=>{
      //     return React.cloneElement(item,{key:i}); 
      //   });
      // }
      // else if(whatIsIt(children) === propTypesList.array){
      //   displayChildren = [...children].map((a)=>a);
      // }

      let displayComponent = ComponentType ? (
        <ComponentType {...originalComponentDetail}>
          {displayChildren}
        </ComponentType>
      ) : null;

      initialCodes.push(
        generateCodeString(
          ComponentType,
          originalComponentDetail,
          displayChildren ? <>{displayChildren}</> : null
        )
      );

      let componentDecorator = decoratorSomething(decoratorItem, displayComponent);
      let decoratedComponentItem = decoratorSomething(
        decorateCommonComponentItem,
        componentDecorator
      );

      let itemClassName = `${styles.tabs_component__item} ${commonDecoratorItemClassName} ${decoratorClassName}`;

      return (
        <div key={index} className={itemClassName}>
          {decoratedComponentItem}
        </div>
      );
    });

  let listClassName = decoratorListClassName || "";
  let itemListClassName = `${styles.tabs_components_list} ${listClassName}`;

  return (
    <div className={styles.tabs_components_list_view}>
      <div className={itemListClassName}>
        {props.locator}
        {decoratorSomething(decorateList, componentsList)}
      </div>
      {props.disableSourceCode ? null : (
        <CodePlayer
          initialCodes={initialCodes}
          isShowFullSource={props.isShowFullSource}
        />
      )}
    </div>
  );
};
