// @flow
import {renderToString} from 'react-dom/server'
import styled, {
  css,
  ThemeProvider,
  withTheme,
  keyframes,
  ServerStyleSheet,
  StyleSheetManager
} from 'styled-components'
import React from 'react'
import type {
  Theme,
  Interpolation,
  ReactComponentFunctional,
  ReactComponentFunctionalUndefinedDefaultProps,
  ReactComponentClass,
  ReactComponentStyled,
  ReactComponentStyledTaggedTemplateLiteral,
  ReactComponentUnion,
  ReactComponentIntersection,
} from 'styled-components'
import { createReadStream } from 'fs'

const TitleTaggedTemplateLiteral: ReactComponentStyledTaggedTemplateLiteral<{}> = styled.h1;

const TitleStyled: ReactComponentStyled<*> = styled.h1`
  font-size: 1.5em;
`;

const TitleGeneric: ReactComponentIntersection<*> = styled.h1`
  font-size: 1.5em;
`;
``
const TitleFunctional: ReactComponentFunctional<*> = styled.h1`
  font-size: 1.5em;
`;

const TitleClass: ReactComponentClass<*> = styled.h1`
  font-size: 1.5em;
`;

declare var needsReactComponentFunctional: ReactComponentFunctional<*> => void
declare var needsReactComponentClass: ReactComponentClass<*> => void

needsReactComponentFunctional(TitleStyled)
needsReactComponentClass(TitleStyled)

const ExtendedTitle: ReactComponentIntersection<*> = styled(TitleStyled)`
  font-size: 2em;
`;

const Wrapper: ReactComponentIntersection<*> = styled.section`
  padding: 4em;
  background: ${({theme}) => theme.background};
`;

// ---- EXTEND ----
const Attrs0ReactComponent: ReactComponentStyled<*> = styled.div.extend``;
const Attrs0ExtendReactComponent: ReactComponentIntersection<*> = Attrs0ReactComponent.extend``;

const Attrs0SyledComponent: ReactComponentStyledTaggedTemplateLiteral<*> = styled.div;
const Attrs0ExtendStyledComponent: ReactComponentIntersection<*> = Attrs0SyledComponent.extend``;

// ---- ATTRIBUTES ----
const Attrs1: ReactComponentStyledTaggedTemplateLiteral<*> = styled.section.attrs({
  testProp: 'foo'
});

// $FlowExpectedError
const Attrs1Error: ReactComponentStyledTaggedTemplateLiteral<*> = styled.section.attrs({
  testProp: 'foo'
})``;

declare var needsString: string => void

needsReactComponentFunctional(styled.section.attrs({})``)
needsReactComponentClass(styled.section.attrs({})``)

// $FlowExpectedError
needsString(styled.section.attrs({})``)

const Attrs2: ReactComponentStyledTaggedTemplateLiteral<*> = styled.section
  .attrs({
    testProp1: 'foo'
  })
  .attrs({
    testProp2: 'bar'
  });

const Attrs3Styled: ReactComponentStyled<*> = styled.section.attrs({
  testProp: 'foo'
})`
  background-color: red;
`;

const Attrs3Generic: ReactComponentIntersection<*> = styled.section.attrs({
  testProp: 'foo'
})`
  background-color: red;
`;

const Attrs3Functional: ReactComponentFunctional<*> = styled.section.attrs({
  testProp: 'foo'
})`
  background-color: red;
`;

const Attrs3Class: ReactComponentClass<*> = styled.section.attrs({
  testProp: 'foo'
})`
  background-color: red;
`;

const theme: Theme = {
  background: "papayawhip"
};

// ---- WithComponent ----
const withComponent1: ReactComponentStyled<*> = styled.div.withComponent('a');
const withComponent2: ReactComponentStyled<*> = styled.div.withComponent(withComponent1);
const withComponent3: ReactComponentStyled<*> = styled.div.withComponent(Attrs3Class);
const withComponent4: ReactComponentStyled<*> = styled('div').withComponent('a');
const withComponent5: ReactComponentStyled<*> = styled('div').withComponent(withComponent1);
const withComponent6: ReactComponentStyled<*> = styled('div').withComponent(Attrs3Class);
// $FlowExpectedError
const withComponentError1: ReactComponentStyled<*> = styled.div.withComponent(0);
// $FlowExpectedError
const withComponentError2: ReactComponentStyled<*> = styled.div.withComponent('NotHere');

class CustomComponentError3 extends React.Component<{ foo: string }> {
  render() { return <div />; }
}

// $FlowExpectedError
const withComponentError3 = styled(CustomComponentError3).withComponent('a');
// $FlowExpectedError
const withComponentError4 = styled(CustomComponentError3).withComponent(withComponent1);
// $FlowExpectedError
const withComponentError5 = styled(CustomComponentError3).withComponent(Attrs3Class);
// $FlowExpectedError
const withComponentError6 = styled(CustomComponentError3).withComponent(0);
// $FlowExpectedError
const withComponentError7 = styled(CustomComponentError3).withComponent('NotHere');

// ---- WithTheme ----
const Component: ReactComponentFunctionalUndefinedDefaultProps<{ theme: Theme }> = ({ theme }) => (
  <ThemeProvider theme={theme}>
    <Wrapper>
      <TitleStyled>Hello World, this is my first styled component!</TitleStyled>
    </Wrapper>
  </ThemeProvider>
);

const ComponentWithTheme: ReactComponentFunctionalUndefinedDefaultProps<{}> = withTheme(Component);

const Component2: ReactComponentFunctionalUndefinedDefaultProps<{}> = () => (
  <ThemeProvider theme={outerTheme => outerTheme}>
    <Wrapper>
      <TitleStyled>Hello World, this is my first styled component!</TitleStyled>
    </Wrapper>
  </ThemeProvider>
);

const OpacityKeyFrame: string = keyframes`
  0%   { opacity: 0; }
  100% { opacity: 1; }
`;

// $FlowExpectedError
const NoExistingElementWrapper = styled.nonexisting`
  padding: 4em;
  background: papayawhip;
`;

const num: 9 = 9

// $FlowExpectedError
const NoExistingComponentWrapper = styled()`
  padding: 4em;
  background: papayawhip;
`;

// $FlowExpectedError
const NumberWrapper = styled(num)`
  padding: 4em;
  background: papayawhip;
`;

const sheet = new ServerStyleSheet()
const html = renderToString(sheet.collectStyles(<ComponentWithTheme />))
const css1 = sheet.getStyleTags()

const sheet2 = new ServerStyleSheet()
const html2 = renderToString(
  <StyleSheetManager sheet={sheet}>
    <ComponentWithTheme />
  </StyleSheetManager>
)

const css2 = sheet.getStyleTags()
const css3 = sheet.getStyleElement()

const stream = createReadStream('file.txt')

// $FlowExpectedError (Must pass in a readable stream)
sheet.interleaveWithNodeStream('file.txt')
sheet.interleaveWithNodeStream(stream)

// ---- COMPONENT CLASS TESTS ----
class NeedsThemeReactClass extends React.Component<{ foo: string, theme: Theme }> {
  render() { return <div />; }
}

class ReactClass extends React.Component<{ foo: string }> {
  render() { return <div />; }
}

const StyledClass: ReactComponentClass<{ foo: string, theme: Theme }> = styled(NeedsThemeReactClass)`
  color: red;
`;

// ---- INTERPOLATION TESTS ----
const interpolation: Array<Interpolation> = styled.css`
  background-color: red;
`;

const interpolation2: Array<Interpolation> = css`
  background-color: red;
`;

// $FlowExpectedError
const interpolationError: Array<Interpolation | boolean> = styled.css`
  background-color: red;
`;

// $FlowExpectedError
const interpolationError2: Array<Interpolation | boolean> = css`
  background-color: red;
`;

// ---- DEFAULT COMPONENT TESTS ----
const defaultComponent: ReactComponentIntersection<{}> = styled.div`
  background-color: red;
`;

// $FlowExpectedError
const defaultComponentError: {} => string = styled.div`
  background-color: red;
`;

// ---- FUNCTIONAL COMPONENT TESTS ----
const FunctionalComponent: ReactComponentFunctionalUndefinedDefaultProps<{ foo: string, theme: Theme }> = props => <div />;

const NeedsFoo1: ReactComponentFunctionalUndefinedDefaultProps<{ foo: string, theme: Theme }> = styled(FunctionalComponent)`
  background-color: red;
`;
// $FlowExpectedError
const NeedsFoo1Error: ReactComponentFunctionalUndefinedDefaultProps<{ foo: number }> = styled(FunctionalComponent)`
  background-color: red;
`;

const NeedsFoo2: ReactComponentFunctionalUndefinedDefaultProps<{ foo: string, theme: Theme }> = styled(NeedsFoo1)`
  background-color: red;
`;
// $FlowExpectedError
const NeedsFoo2Error: ReactComponentFunctionalUndefinedDefaultProps<{ foo: number }> = styled(NeedsFoo1)`
  background-color: red;
`;

const NeedsNothingInferred = styled(() => <div />);
