<?php
/**
 * Flickr OAuth 1.0a Authentication
 * Handles authorization flow to get access tokens for API calls
 */
class FlickrOAuth
{
    private $consumerKey;
    private $consumerSecret;
    private $requestTokenUrl = 'https://www.flickr.com/services/oauth/request_token';
    private $authorizeUrl = 'https://www.flickr.com/services/oauth/authorize';
    private $accessTokenUrl = 'https://www.flickr.com/services/oauth/access_token';

    private $oauthToken = null;
    private $oauthTokenSecret = null;
    private $tokenFile;

    public function __construct($consumerKey, $consumerSecret)
    {
        $this->consumerKey = $consumerKey;
        $this->consumerSecret = $consumerSecret;
        $this->tokenFile = __DIR__ . '/../data/oauth_token.json';

        // Load saved tokens if exist
        $this->loadTokens();
    }

    /**
     * Check if we have valid access tokens
     */
    public function isAuthorized()
    {
        return !empty($this->oauthToken) && !empty($this->oauthTokenSecret);
    }

    /**
     * Get stored OAuth token
     */
    public function getOAuthToken()
    {
        return $this->oauthToken;
    }

    /**
     * Get stored OAuth token secret
     */
    public function getOAuthTokenSecret()
    {
        return $this->oauthTokenSecret;
    }

    /**
     * Step 1: Get request token and return authorization URL
     */
    public function getAuthorizationUrl($callbackUrl)
    {
        $params = [
            'oauth_callback' => $callbackUrl,
            'oauth_consumer_key' => $this->consumerKey,
            'oauth_nonce' => $this->generateNonce(),
            'oauth_signature_method' => 'HMAC-SHA1',
            'oauth_timestamp' => time(),
            'oauth_version' => '1.0',
        ];

        $params['oauth_signature'] = $this->generateSignature('GET', $this->requestTokenUrl, $params);

        $url = $this->requestTokenUrl . '?' . http_build_query($params);

        $response = $this->httpRequest($url);

        if (!$response) {
            throw new Exception('Failed to get request token');
        }

        parse_str($response, $data);

        if (!isset($data['oauth_token']) || !isset($data['oauth_token_secret'])) {
            throw new Exception('Invalid request token response: ' . $response);
        }

        // Store request token temporarily (needed for step 2)
        $_SESSION['flickr_request_token'] = $data['oauth_token'];
        $_SESSION['flickr_request_token_secret'] = $data['oauth_token_secret'];

        // Return URL for user to authorize
        return $this->authorizeUrl . '?oauth_token=' . $data['oauth_token'] . '&perms=read';
    }

    /**
     * Step 2: Exchange verifier for access token
     */
    public function handleCallback($oauthToken, $oauthVerifier)
    {
        if (!isset($_SESSION['flickr_request_token_secret'])) {
            throw new Exception('Request token secret not found in session');
        }

        $requestTokenSecret = $_SESSION['flickr_request_token_secret'];

        $params = [
            'oauth_consumer_key' => $this->consumerKey,
            'oauth_token' => $oauthToken,
            'oauth_verifier' => $oauthVerifier,
            'oauth_nonce' => $this->generateNonce(),
            'oauth_signature_method' => 'HMAC-SHA1',
            'oauth_timestamp' => time(),
            'oauth_version' => '1.0',
        ];

        $params['oauth_signature'] = $this->generateSignature(
            'GET',
            $this->accessTokenUrl,
            $params,
            $requestTokenSecret
        );

        $url = $this->accessTokenUrl . '?' . http_build_query($params);

        $response = $this->httpRequest($url);

        if (!$response) {
            throw new Exception('Failed to get access token');
        }

        parse_str($response, $data);

        if (!isset($data['oauth_token']) || !isset($data['oauth_token_secret'])) {
            throw new Exception('Invalid access token response: ' . $response);
        }

        // Save access tokens
        $this->oauthToken = $data['oauth_token'];
        $this->oauthTokenSecret = $data['oauth_token_secret'];
        $this->saveTokens($data);

        // Clean up session
        unset($_SESSION['flickr_request_token']);
        unset($_SESSION['flickr_request_token_secret']);

        return [
            'oauth_token' => $this->oauthToken,
            'user_nsid' => $data['user_nsid'] ?? null,
            'username' => $data['username'] ?? null,
            'fullname' => $data['fullname'] ?? null,
        ];
    }

    /**
     * Sign an API request with OAuth
     */
    public function signRequest($method, $url, $params = [])
    {
        $oauthParams = [
            'oauth_consumer_key' => $this->consumerKey,
            'oauth_token' => $this->oauthToken,
            'oauth_nonce' => $this->generateNonce(),
            'oauth_signature_method' => 'HMAC-SHA1',
            'oauth_timestamp' => time(),
            'oauth_version' => '1.0',
        ];

        $allParams = array_merge($params, $oauthParams);
        $oauthParams['oauth_signature'] = $this->generateSignature(
            $method,
            $url,
            $allParams,
            $this->oauthTokenSecret
        );

        return $oauthParams;
    }

    /**
     * Generate OAuth signature
     */
    private function generateSignature($method, $url, $params, $tokenSecret = '')
    {
        ksort($params);

        $paramString = http_build_query($params, '', '&', PHP_QUERY_RFC3986);

        $baseString = strtoupper($method) . '&'
            . rawurlencode($url) . '&'
            . rawurlencode($paramString);

        $signingKey = rawurlencode($this->consumerSecret) . '&' . rawurlencode($tokenSecret);

        return base64_encode(hash_hmac('sha1', $baseString, $signingKey, true));
    }

    /**
     * Generate random nonce
     */
    private function generateNonce()
    {
        return md5(uniqid(mt_rand(), true));
    }

    /**
     * Make HTTP request
     */
    private function httpRequest($url)
    {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => true,
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode !== 200) {
            return false;
        }

        return $response;
    }

    /**
     * Save tokens to file
     */
    private function saveTokens($data)
    {
        $dir = dirname($this->tokenFile);
        if (!is_dir($dir)) {
            mkdir($dir, 0700, true);
        }

        $tokenData = [
            'oauth_token' => $data['oauth_token'],
            'oauth_token_secret' => $data['oauth_token_secret'],
            'user_nsid' => $data['user_nsid'] ?? null,
            'username' => $data['username'] ?? null,
            'created_at' => date('Y-m-d H:i:s'),
        ];

        file_put_contents($this->tokenFile, json_encode($tokenData, JSON_PRETTY_PRINT));
        chmod($this->tokenFile, 0600);
    }

    /**
     * Load tokens from file
     */
    private function loadTokens()
    {
        if (file_exists($this->tokenFile)) {
            $data = json_decode(file_get_contents($this->tokenFile), true);
            if ($data) {
                $this->oauthToken = $data['oauth_token'] ?? null;
                $this->oauthTokenSecret = $data['oauth_token_secret'] ?? null;
            }
        }
    }

    /**
     * Clear saved tokens (logout)
     */
    public function clearTokens()
    {
        $this->oauthToken = null;
        $this->oauthTokenSecret = null;
        if (file_exists($this->tokenFile)) {
            unlink($this->tokenFile);
        }
    }
}
