import fs from 'fs/promises';
import path from 'path';
import {
  ConfigPlugin,
  IOSConfig,
  WarningAggregator,
  withDangerousMod,
  withXcodeProject,
} from '@expo/config-plugins';
import { ExpoConfig } from '@expo/config-types';

async function validateOCRBlobAssetDir(ocrBlobsDirPath: string): Promise<void> {
  return new Promise(async (resolve, reject) => {
    const stat = await fs.stat(ocrBlobsDirPath);
    if (stat.isDirectory()) {
      const dir = await fs.readdir(ocrBlobsDirPath);
      for (const file of dir) {
        if (path.extname(path.join(ocrBlobsDirPath, file)) !== '.traineddata') {
          reject(new Error(`File ${file} is not a valid train file`));
        }
      }
      resolve();
    } else {
      reject(`${ocrBlobsDirPath} is not a valid directory`);
    }
  });
}

async function copyAndroidAssets(ocrBlobsDirPath: string, platformProjectRoot: string) {
  return fs.cp(
    ocrBlobsDirPath,
    path.resolve(platformProjectRoot, 'app', 'src', 'main', 'assets', 'ocr_blobs'),
    {
      recursive: true,
    }
  );
}

const withAssetsAndroid: ConfigPlugin<string> = (config, ocrBlobsDirPath) => {
  return withDangerousMod(config, [
    'android',
    async (config) => {
      try {
        const resolvedOcrBlobsDirPath = path.resolve(
          config.modRequest.projectRoot,
          ocrBlobsDirPath
        );
        await validateOCRBlobAssetDir(resolvedOcrBlobsDirPath);
        await copyAndroidAssets(resolvedOcrBlobsDirPath, config.modRequest.platformProjectRoot);
      } catch (error: any) {
        WarningAggregator.addWarningAndroid(
          'react-native-scanbot-sdk',
          `OCR Blobs could not be installed on Android, ${error.message ?? 'Unknown error'}`
        );
      }

      return config;
    },
  ]);
};

async function copyIOSAssets(ocrBlobsDirPath: string, platformProjectRoot: string) {
  const bundlePath = path.resolve(platformProjectRoot, 'ScanbotSDKOCRData.bundle');
  if (!(await fs.stat(ocrBlobsDirPath)).isDirectory()) {
    await fs.mkdir(bundlePath);
  }
  const dir = await fs.readdir(ocrBlobsDirPath);
  for (const file of dir) {
    await fs.cp(path.join(ocrBlobsDirPath, file), path.join(bundlePath, file));
  }
}

const withXcodeProjectAssets = (config: ExpoConfig) => {
  return withXcodeProject(config, (props) => {
    try {
      const { projectName } = props.modRequest;
      const xcodeProject = props.modResults;

      props.modResults = IOSConfig.XcodeUtils.addResourceFileToGroup({
        filepath: 'ScanbotSDKOCRData.bundle',
        groupName: `${projectName}/Resources`,
        isBuildFile: true,
        project: xcodeProject,
        verbose: true,
      });

      return props;
    } catch (error: any) {
      WarningAggregator.addWarningIOS(
        'react-native-scanbot-sdk',
        `OCR Blobs could not be added to XcodeProject, ${error.message ?? 'Unknown error'}`
      );
    }

    return props;
  });
};

const withAssetsIOS: ConfigPlugin<string> = (config, ocrBlobsDirPath) => {
  config = withDangerousMod(config, [
    'ios',
    async (config) => {
      let configWithAssets = config;
      try {
        const resolvedOcrBlobsDirPath = path.resolve(
          config.modRequest.projectRoot,
          ocrBlobsDirPath
        );
        await validateOCRBlobAssetDir(resolvedOcrBlobsDirPath);
        await copyIOSAssets(resolvedOcrBlobsDirPath, config.modRequest.platformProjectRoot);
      } catch (error: any) {
        WarningAggregator.addWarningIOS(
          'react-native-scanbot-sdk',
          `OCR Blobs could not be installed on iOS, ${error.message ?? 'Unknown error'}`
        );
      }

      return configWithAssets;
    },
  ]);

  return withXcodeProjectAssets(config);
};

export const withAssets: ConfigPlugin<{ ocrBlobsDirPath?: string }> = (
  config,
  { ocrBlobsDirPath } = {}
) => {
  if (ocrBlobsDirPath) {
    config = withAssetsAndroid(config, ocrBlobsDirPath);
    config = withAssetsIOS(config, ocrBlobsDirPath);
  }

  return config;
};
