import type { GoogleDriveOptionsWithDefaults } from './google-drive-types.ts';
import {
    ensureFolderExists,
    readFolder,
    createEmptyFile,
    fillFileIfEtagMatches,
    readJsonFileContent
} from './google-drive-helper.ts';
import { newRxError } from '../../rx-error.ts';
import { randomToken } from '../utils/utils-string.ts';
import { TRANSACTION_FILE_NAME } from './transaction.ts';
import { ensureNotFalsy } from '../utils/index.ts';
import { WAL_FILE_NAME } from './upstream.ts';

const NON_ALLOWED_ROOT_FOLDERS = [
    '/',
    '',
    null,
    false,
    undefined,
    'root'
];

export async function initDriveStructure(
    googleDriveOptions: GoogleDriveOptionsWithDefaults
) {

    /**
     * The appDataFolder is a dedicated, hidden per-app space.
     * Storing data directly in its root is allowed, so an empty
     * folderPath is valid there. For the regular 'drive' space we
     * must not use the root folder.
     */
    if (googleDriveOptions.space !== 'appDataFolder') {
        NON_ALLOWED_ROOT_FOLDERS.forEach(nonAllowed => {
            if (googleDriveOptions.folderPath === nonAllowed) {
                throw newRxError('GDR1', {
                    folderPath: googleDriveOptions.folderPath
                });
            }
        });
    }


    // root folder
    const rootFolderId = await ensureFolderExists(googleDriveOptions, googleDriveOptions.folderPath);

    const rootFolderContent = await readFolder(googleDriveOptions, googleDriveOptions.folderPath);

    /**
     * Folder but either be empty
     * or already used as a RxDB google-drive sync target.
     */
    const hasRxDBJson = rootFolderContent.find((file: any) => file.name === 'rxdb.json');
    const hasOther = rootFolderContent.find((file: any) => file.name !== 'rxdb.json');
    if (hasOther && !hasRxDBJson) {
        throw newRxError('GDR9', {
            folderPath: googleDriveOptions.folderPath
        });
    }

    /**
     * Create rxdb.json file.
     * This must always be the first step.
     */
    const rxdbJson = await createEmptyFile(googleDriveOptions, rootFolderId, 'rxdb.json');
    let replicationIdentifier: string;
    if (rxdbJson.size === 0) {
        const rxdbJsonData = await fillFileIfEtagMatches<{ replicationIdentifier: string }>(
            googleDriveOptions,
            rxdbJson.fileId,
            rxdbJson.etag,
            { replicationIdentifier: randomToken(10) }
        );
        replicationIdentifier = ensureNotFalsy(rxdbJsonData.content).replicationIdentifier;
    } else {
        const rxdbJsonData = await readJsonFileContent<{ replicationIdentifier: string }>(
            googleDriveOptions,
            rxdbJson.fileId
        );
        replicationIdentifier = ensureNotFalsy(rxdbJsonData.content).replicationIdentifier;
    }


    // docs folder
    const docsFolderId = await ensureFolderExists(googleDriveOptions, googleDriveOptions.folderPath + '/docs');

    // signaling folder
    const signalingFolderId = await ensureFolderExists(googleDriveOptions, googleDriveOptions.folderPath + '/signaling');

    // transaction file
    const transactionFile = await createEmptyFile(
        googleDriveOptions,
        rootFolderId,
        TRANSACTION_FILE_NAME
    );

    // WAL file
    const walFile = await createEmptyFile(
        googleDriveOptions,
        rootFolderId,
        WAL_FILE_NAME
    );

    return {
        rootFolderId,
        docsFolderId,
        signalingFolderId,
        replicationIdentifier,
        rxdbJson,
        transactionFile,
        walFile
    };
}

export type DriveStructure = Awaited<ReturnType<typeof initDriveStructure>>;
