import { microsoftSharePointAuth } from '../auth';
import { createAction, Property } from '@activepieces/pieces-framework';
import { getGraphBaseUrl } from '../common/microsoft-cloud';
import { microsoftSharePointCommon } from '../common';
import { Client, ResponseType } from '@microsoft/microsoft-graph-client';

const polling_delay_ms = 2000; 
const polling_timeout_secs = 120; 

async function delay(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export const copyItemAction = createAction({
  auth: microsoftSharePointAuth,
  name: 'microsoft_sharepoint_copy_item',
  displayName: 'Copy File or Folder (Across Sites)',
  description:
    'Copy a file or folder from one site to another within the same tenant, with overwrite option.',
  audience: 'both',
  aiMetadata: {
    description: 'Copies a file or folder from one SharePoint site/drive to a destination site, drive, and folder within the same tenant, optionally renaming the copy. Use to duplicate content across sites; for same-site copies use the within-site variant. The conflict behavior (fail, replace, or rename) controls what happens on a name clash. Not idempotent: each run produces another copy (rename mode) or overwrites the target (replace mode).',
    idempotent: false,
  },
  props: {

    siteId: microsoftSharePointCommon.siteId,
    driveId: microsoftSharePointCommon.driveId,
    itemId: microsoftSharePointCommon.itemId,

    destination_site_id: microsoftSharePointCommon.siteId,
    destination_drive_id: microsoftSharePointCommon.createDriveDropdown({
      displayName: 'Destination Drive',
      refreshers: ['destination_site_id'],
    }),
    destination_folder_id: microsoftSharePointCommon.destinationFolderId,

    new_name: Property.ShortText({
      displayName: 'New Name (Optional)',
      description: 'A new name for the copied item. If not provided, the original name is used.',
      required: false,
    }),
    conflict_behavior: Property.StaticDropdown({
      displayName: 'Conflict Behavior',
      description: 'Action to take if a file with the same name already exists.',
      required: true,
      options: {
        options: [
          { label: 'Fail on conflict', value: 'fail' },
          { label: 'Overwrite existing file', value: 'replace' },
          { label: 'Rename with a number', value: 'rename' },
        ],
      },
      defaultValue: 'fail',
    }),
  },
  async run(context) {
    const {
      siteId,
      driveId,
      itemId,
      destination_drive_id,
      destination_folder_id,
      new_name,
      conflict_behavior,
    } = context.propsValue;

    const cloud = context.auth.props?.['cloud'] as string | undefined;
    const client = Client.initWithMiddleware({
      authProvider: {
        getAccessToken: () => Promise.resolve(context.auth.access_token),
      },
      baseUrl: getGraphBaseUrl(cloud),
    });

    const body: { parentReference: { driveId: string; id?: string }; name?: string } = {
      parentReference: {
        driveId: destination_drive_id,
      },
    };

    if (destination_folder_id) {
      body.parentReference.id = destination_folder_id;
    }
    if (new_name) {
      body.name = new_name;
    }

    let initialResponse;
    try {
      initialResponse = await client
        .api(`/drives/${driveId}/items/${itemId}/copy`)
        .responseType(ResponseType.RAW)
        .query({ '@microsoft.graph.conflictBehavior': conflict_behavior })
        .post(body);
    } catch (error: any) {
      if (error.statusCode === 400) {
        throw new Error(`Invalid request: ${error.message || 'Check your input parameters'}`);
      }
      if (error.statusCode === 403) {
        throw new Error('Insufficient permissions to copy this item. Requires Files.ReadWrite or higher.');
      }
      if (error.statusCode === 404) {
        throw new Error('Source item not found. Please verify the site, drive, and item IDs.');
      }
      throw new Error(`Failed to initiate copy operation: ${error.message || 'Unknown error'}`);
    }

    const monitorUrl = initialResponse.headers.get('Location');
    if (!monitorUrl) {
      throw new Error('Could not get monitor URL from copy operation response.');
    }

    let attempts = polling_timeout_secs * 1000 / polling_delay_ms;
    while (attempts > 0) {
      const monitorResponse = await client.api(monitorUrl).get();
      if (monitorResponse.status === 'completed') {
        return {
          success: true,
          status: 'completed',
          resourceId: monitorResponse.resourceId,
          resourceLocation: monitorResponse.resourceLocation,
        };
      }
      if (monitorResponse.status === 'failed') {
        const errorDetails = monitorResponse.error?.details
          ? monitorResponse.error.details.map((d: any) => d.message || d.code).join(', ')
          : monitorResponse.error?.message || 'Unknown error';
        throw new Error(`Copy operation failed: ${errorDetails}`);
      }
      await delay(polling_delay_ms);
      attempts--;
    }

    throw new Error('Copy operation timed out after 120 seconds.');
  },
});