<?php
namespace PrimeLink\Modules\Imports\DataSources;

if ( ! defined( 'ABSPATH' ) ) { exit; }

final class OneDriveSource implements DataSourceInterface {

    private $settings = array();

    public function __construct( array $settings = array() ) {
        $this->settings = $settings;
    }

    public function handle( array $payload ) {
        $token = $this->settings['onedrive_access_token'] ?? '';
        $refresh_token = $this->settings['onedrive_refresh_token'] ?? '';
        $client_id = $this->settings['onedrive_client_id'] ?? '';
        $client_secret = $this->settings['onedrive_client_secret'] ?? '';
        $tenant_id = $this->settings['onedrive_tenant_id'] ?? '';
        $token_expires = $this->settings['onedrive_token_expires'] ?? 0;
        
        // Check if token is expired and try to refresh
        if ( $token && $refresh_token && $client_id && $client_secret && $tenant_id ) {
            if ( ! $token_expires || time() >= ( $token_expires - 300 ) ) {
                $new_token = $this->refresh_token( $refresh_token, $client_id, $client_secret, $tenant_id );
                if ( ! is_wp_error( $new_token ) && is_string( $new_token ) && ! empty( $new_token ) ) {
                    $token = $new_token;
                    $this->settings['onedrive_access_token'] = $token;
                    update_option( 'mt_primelink_settings', $this->settings );
                }
            }
        }
        
        if ( ! $token ) {
            return array( 'error' => 'OneDrive access token is missing. Please authenticate with OneDrive in Settings → Integrations.' );
        }

        $input = $payload['file'] ?? ( $payload['path'] ?? ( $this->settings['onedrive_default_file'] ?? '' ) );
        $input = trim( (string) $input );
        if ( ! $input ) {
            return array( 'error' => 'No OneDrive item ID or share link provided.' );
        }

        $is_url = filter_var( $input, FILTER_VALIDATE_URL );
        $url    = '';
        $name   = '';

        if ( $is_url ) {
            // For share links we can often append ?download=1 without auth; if token exists we also pass Authorization.
            $url = $input;
            if ( false === stripos( $url, 'download=1' ) ) {
                $url .= ( strpos( $url, '?' ) === false ? '?' : '&' ) . 'download=1';
            }
        } else {
            // Treat as item ID; requires token
            $url = 'https://graph.microsoft.com/v1.0/me/drive/items/' . rawurlencode( $input ) . '/content';
        }

        $response = \PrimeLink\Helpers\HttpHelper::get( $url, array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $token,
            ),
            'timeout'     => 60,
            'retry'       => 1,
            'retry_delay' => 1,
        ) );

        if ( ! $response['success'] ) {
            return array( 'error' => $response['error'] ?? 'Failed to fetch from OneDrive.' );
        }

        $code = $response['code'];
        if ( $code < 200 || $code > 299 ) {
            $body = $response['body'];
            return array( 'error' => 'OneDrive download failed (' . $code . '): ' . wp_strip_all_tags( $body ) );
        }

        $body = $response['body'];
        if ( ! $body ) {
            return array( 'error' => 'OneDrive returned an empty file.' );
        }

        // Try to grab filename from headers
        $disp = isset( $response['headers']['content-disposition'] ) ? $response['headers']['content-disposition'] : null;
        if ( $disp && preg_match( '/filename=\"?([^\";]+)\"?/i', $disp, $m ) ) {
            $name = $m[1];
        } else {
            $name = basename( parse_url( $input, PHP_URL_PATH ) ) ?: 'onedrive-file';
        }

        $upload_dir = wp_upload_dir();
        $dest_dir   = trailingslashit( $upload_dir['basedir'] ) . 'primelink/';
        $dest_url   = trailingslashit( $upload_dir['baseurl'] ) . 'primelink/';
        if ( ! \PrimeLink\Helpers\FilesystemHelper::directory_exists( $dest_dir ) ) {
            \PrimeLink\Helpers\FilesystemHelper::create_directory( $dest_dir );
        }

        $sanitized_name = sanitize_file_name( $name );
        if ( ! $sanitized_name ) {
            $sanitized_name = 'onedrive-file.csv';
        }
        $dest_name = time() . '-' . $sanitized_name;
        $dest_path = $dest_dir . $dest_name;

        \PrimeLink\Helpers\FilesystemHelper::write_file( $dest_path, $body );

        $ext  = strtolower( pathinfo( $sanitized_name, PATHINFO_EXTENSION ) );
        $type = in_array( $ext, array( 'xml', 'xlsx', 'xls', 'json', 'csv' ), true ) ? $ext : 'csv';

        return array(
            'path'          => $dest_path,
            'url'           => $dest_url . $dest_name,
            'type'          => $type,
            'original_name' => $sanitized_name,
            'source'        => 'onedrive',
            'downloaded_at' => current_time( 'mysql' ),
        );
    }

    /**
     * Refresh OneDrive access token.
     *
     * @param string $refresh_token Refresh token.
     * @param string $client_id     Client ID.
     * @param string $client_secret Client Secret.
     * @param string $tenant_id     Tenant ID.
     * @return string|\WP_Error New access token or error.
     */
    private function refresh_token( $refresh_token, $client_id, $client_secret, $tenant_id ) {
        $token_url = 'https://login.microsoftonline.com/' . $tenant_id . '/oauth2/v2.0/token';

        $response = wp_remote_post( $token_url, array(
            'body' => array(
                'client_id'     => $client_id,
                'client_secret' => $client_secret,
                'refresh_token' => $refresh_token,
                'grant_type'    => 'refresh_token',
            ),
            'timeout' => 30,
        ) );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $body = wp_remote_retrieve_body( $response );
        $data = json_decode( $body, true );

        if ( ! isset( $data['access_token'] ) ) {
            return new \WP_Error( 'token_refresh_failed', 'Failed to refresh OneDrive access token.' );
        }

        // Update token expiration in settings
        if ( isset( $data['expires_in'] ) ) {
            $this->settings['onedrive_token_expires'] = time() + absint( $data['expires_in'] );
        }
        if ( isset( $data['refresh_token'] ) ) {
            $this->settings['onedrive_refresh_token'] = $data['refresh_token'];
        }
        $this->settings['onedrive_access_token'] = $data['access_token'];
        update_option( 'mt_primelink_settings', $this->settings );

        return $data['access_token'];
    }
}
