import { z } from 'zod';
import { globalOptionsZod } from '../../../Command.js';
import request from '../../../request.js';
import { urlUtil } from '../../../utils/urlUtil.js';
import { spo } from '../../../utils/spo.js';
import { validation } from '../../../utils/validation.js';
import GraphCommand from '../../base/GraphCommand.js';
import commands from '../commands.js';
const nameConflictBehaviorOptions = ['fail', 'replace', 'rename'];
export const options = z.strictObject({
    ...globalOptionsZod.shape,
    webUrl: z.string()
        .refine(url => validation.isValidSharePointUrl(url) === true, {
        error: e => `'${e.input}' is not a valid SharePoint Online site URL.`
    })
        .alias('u'),
    sourceUrl: z.string().alias('s'),
    targetUrl: z.string().alias('t'),
    newName: z.string().optional(),
    nameConflictBehavior: z.enum(nameConflictBehaviorOptions).optional()
});
class FileCopyCommand extends GraphCommand {
    get name() {
        return commands.COPY;
    }
    get description() {
        return 'Copies a file to another location using the Microsoft Graph';
    }
    get schema() {
        return options;
    }
    async commandAction(logger, args) {
        try {
            const { webUrl, sourceUrl, targetUrl, newName, verbose } = args.options;
            const sourcePath = this.getAbsoluteUrl(webUrl, sourceUrl);
            const destinationPath = this.getAbsoluteUrl(webUrl, targetUrl);
            if (this.verbose) {
                await logger.logToStderr(`Copying file '${sourcePath}' to '${destinationPath}'...`);
            }
            const copyUrl = await this.getCopyUrl(args.options, sourcePath, logger);
            const { targetDriveId, targetItemId } = await this.getTargetDriveAndItemId(webUrl, targetUrl, logger, verbose);
            const requestOptions = {
                url: copyUrl,
                headers: {
                    accept: 'application/json;odata.metadata=none'
                },
                responseType: 'json',
                data: {
                    parentReference: {
                        driveId: targetDriveId,
                        id: targetItemId
                    }
                }
            };
            if (newName) {
                const sourceFileName = sourcePath.substring(sourcePath.lastIndexOf('/') + 1);
                const sourceFileExtension = sourceFileName.includes('.') ? sourceFileName.substring(sourceFileName.lastIndexOf('.')) : '';
                const newNameExtension = newName.includes('.') ? newName.substring(newName.lastIndexOf('.')) : '';
                requestOptions.data.name = newNameExtension ? `${newName.replace(newNameExtension, '')}${sourceFileExtension}` : `${newName}${sourceFileExtension}`;
            }
            await request.post(requestOptions);
        }
        catch (err) {
            this.handleRejectedODataJsonPromise(err);
        }
    }
    async getCopyUrl(options, sourcePath, logger) {
        const { webUrl, sourceUrl, verbose, nameConflictBehavior } = options;
        const folderUrl = new URL(sourcePath);
        const siteId = await spo.getSiteIdByMSGraph(webUrl, logger, verbose);
        const drive = await this.getDocumentLibrary(siteId, folderUrl, sourceUrl, logger);
        const itemId = await this.getStartingFolderId(drive, folderUrl, logger);
        const queryParameters = nameConflictBehavior && nameConflictBehavior !== 'fail'
            ? `@microsoft.graph.conflictBehavior=${nameConflictBehavior}`
            : '';
        const copyUrl = `${this.resource}/v1.0/sites/${siteId}/drives/${drive.id}/items/${itemId}/copy${queryParameters ? `?${queryParameters}` : ''}`;
        return copyUrl;
    }
    async getTargetDriveAndItemId(webUrl, targetUrl, logger, verbose) {
        const targetSiteUrl = urlUtil.getTargetSiteAbsoluteUrl(webUrl, targetUrl);
        const targetSiteId = await spo.getSiteIdByMSGraph(targetSiteUrl, logger, verbose);
        const targetFolderUrl = new URL(this.getAbsoluteUrl(targetSiteUrl, targetUrl));
        const targetDrive = await this.getDocumentLibrary(targetSiteId, targetFolderUrl, targetUrl, logger);
        const targetDriveId = targetDrive.id;
        const targetItemId = await this.getStartingFolderId(targetDrive, targetFolderUrl, logger);
        return { targetDriveId, targetItemId };
    }
    async getDocumentLibrary(siteId, folderUrl, folderUrlFromUser, logger) {
        if (this.verbose) {
            await logger.logToStderr(`Getting document library...`);
        }
        const requestOptions = {
            url: `${this.resource}/v1.0/sites/${siteId}/drives?$select=webUrl,id`,
            headers: {
                accept: 'application/json;odata.metadata=none'
            },
            responseType: 'json'
        };
        const drives = await request.get(requestOptions);
        const lowerCaseFolderUrl = folderUrl.href.toLowerCase();
        const drive = drives.value
            .sort((a, b) => b.webUrl.localeCompare(a.webUrl))
            .find((d) => {
            const driveUrl = d.webUrl.toLowerCase();
            // ensure that the drive url is a prefix of the folder url
            return lowerCaseFolderUrl.startsWith(driveUrl) &&
                (driveUrl.length === lowerCaseFolderUrl.length ||
                    lowerCaseFolderUrl[driveUrl.length] === '/');
        });
        if (!drive) {
            throw `Document library '${folderUrlFromUser}' not found`;
        }
        return drive;
    }
    async getStartingFolderId(documentLibrary, folderUrl, logger) {
        if (this.verbose) {
            await logger.logToStderr(`Getting starting folder id...`);
        }
        const documentLibraryRelativeFolderUrl = folderUrl.href.replace(new RegExp(`${documentLibrary.webUrl}`, 'i'), '').replace(/\/+$/, '');
        const requestOptions = {
            url: `${this.resource}/v1.0/drives/${documentLibrary.id}/root${documentLibraryRelativeFolderUrl ? `:${documentLibraryRelativeFolderUrl}` : ''}?$select=id`,
            headers: {
                accept: 'application/json;odata.metadata=none'
            },
            responseType: 'json'
        };
        const folder = await request.get(requestOptions);
        return folder?.id;
    }
    getAbsoluteUrl(webUrl, url) {
        return url.startsWith('https://') ? url : urlUtil.getAbsoluteUrl(webUrl, url);
    }
}
export default new FileCopyCommand();
//# sourceMappingURL=file-copy.js.map