import * as React from 'react'
import { Subscription } from 'rxjs'
import { StoreDefinition } from '../'
import { Diff, equals, getDisplayName, keys, mapValues, some } from '../utils'

/**
 * @deprecated Use `createConnectedStoreAs` instead.
 */
export function connectAs<
  Stores extends { [alias: string]: StoreDefinition<any> }
>(stores: Stores) {
  return function<Props extends object>(
    Component: React.ComponentType<
      { [K in keyof Stores]: ReturnType<Stores[K]['toStore']> } & Props
    >
  ): React.ComponentClass<Diff<Props, Stores>> {
    type State = {
      stores: {
        [K in keyof Stores]: ReturnType<Stores[K]['getCurrentSnapshot']>
      }
      subscriptions: Subscription[]
    }

    return class extends React.Component<Diff<Props, Stores>, State> {
      static displayName = `withStore(${getDisplayName(Component)})`
      state = {
        stores: mapValues(
          stores,
          _ =>
            _.getCurrentSnapshot() as ReturnType<
              (typeof _)['getCurrentSnapshot']
            >
        ),
        subscriptions: keys(stores).map(k =>
          stores[k].onAll().subscribe(({ previousValue, value }) => {
            if (equals(previousValue, value)) {
              return false
            }
            this.setState(state => ({
              stores: Object.assign({}, state.stores as any, {
                [k]: stores[k].getCurrentSnapshot()
              })
            }))
          })
        )
      }
      componentWillUnmount() {
        this.state.subscriptions.forEach(_ => _.unsubscribe())
      }
      shouldComponentUpdate(props: Diff<Props, Stores>, state: State) {
        return (
          some(state.stores, (s, k) => s !== this.state.stores[k]) ||
          Object.keys(props).some(
            _ => (props as any)[_] !== (this.props as any)[_]
          )
        )
      }
      render() {
        return <Component {...this.props as any} {...this.state.stores} />
      }
    }
  }
}
