import { ReactNode, createContext, useContext, useState } from "react";
import { useOAuth } from "./OAuth2";
import { STSClient, AssumeRoleWithWebIdentityCommand } from "@aws-sdk/client-sts";
import { S3Client } from "@aws-sdk/client-s3";
import { AwsCredentialIdentity } from "@aws-sdk/types";
import { useCallback } from "react";
import { useEffect } from "react";

// **** AWS Config ****
export interface AWSConfig {
  endpoint: string;
  region: string;
}

// ***** State *****

export interface IS3ServiceState {
  awsCredentials?: AwsCredentialIdentity;
}


export const initialS3ServiceState: IS3ServiceState = {
  awsCredentials: undefined,
}

// ***** Context *****

export interface S3ContextProps {
  awsConfig: AWSConfig;
  client: S3Client;
  isAuthenticated: () => boolean;
}

export const S3ServiceContext = createContext<S3ContextProps | undefined>(undefined);

// ***** S3Service *****

interface S3ServiceProviderProps {
  awsConfig: AWSConfig;
  children?: ReactNode;
}

export const S3ServiceProvider = (props: S3ServiceProviderProps): JSX.Element => {
  const { children, awsConfig } = props;

  const [
    s3ServiceState,
    setS3ServiceState
  ] = useState<IS3ServiceState>(initialS3ServiceState);

  const oAuth = useOAuth();
  const { awsCredentials } = s3ServiceState;

  // Factory method
  const getS3Client = useCallback(() => {
    return new S3Client({
      endpoint: awsConfig.endpoint,
      region: awsConfig.region,
      credentials: awsCredentials,
      forcePathStyle: true
    });
  }, [awsCredentials, awsConfig.endpoint]);


  const isAuthenticated = () => {
    return oAuth.isAuthenticated && !!awsCredentials;
  }


  // Exchange token for AWS Credentiasl with AssumeRoleWebIdendity
  useEffect(() => {
    const token = oAuth.user?.token;

    if (!(oAuth.isAuthenticated && token)) {
      console.log("Token missig or expired");
      return;
    }

    const sts = new STSClient({ ...awsConfig });
    const command = new AssumeRoleWithWebIdentityCommand({
      DurationSeconds: 3600,
      RoleArn: "arn:aws:iam:::role/S3AccessIAM200",
      RoleSessionName: "ceph-frontend-poc", // TODO: change me
      WebIdentityToken: token.access_token,
    });

    sts.send(command)
      .then(response => {
        const { Credentials } = response;
        if (!Credentials) {
          throw new Error("Credentials not found");
        }
        const { AccessKeyId, SecretAccessKey, SessionToken } = Credentials;
        if (AccessKeyId && SecretAccessKey && SessionToken) {
          setS3ServiceState({
            awsCredentials: {
              accessKeyId: AccessKeyId,
              secretAccessKey: SecretAccessKey,
              sessionToken: SessionToken
            }
          });
        } else {
          console.warn("Warning: some one or more AWS Credentials member is empty");
        }
      }).catch(err => {
        console.error("Cannot retrieve AWS Credentials from STS", err);
      });
  }, [awsConfig, oAuth.isAuthenticated, oAuth.user]);


  return (
    <S3ServiceContext.Provider value={{
      awsConfig: awsConfig,
      client: getS3Client(),
      isAuthenticated: () => isAuthenticated()
    }}>
      {children}
    </S3ServiceContext.Provider>
  );
}

// **** useS3Service *****

export const useS3Service = (): S3ContextProps => {
  const context = useContext(S3ServiceContext);
  if (!context) {
    throw new Error(
      "S3ServiceProvider context is undefined, " +
      "please verify you are calling useS3Service " +
      "as a child of <S3ServicePrivder> comonent."
    );
  }
  return context;
}