<?php
/**
 * Flickr API Client - fetches photos from your Flickr account
 * Compatible with PHP 7.2+
 * Supports OAuth for accessing original quality photos
 */

class FlickrAPI
{
    private $apiKey;
    private $apiSecret;
    private $userId;
    private $baseUrl = 'https://api.flickr.com/services/rest/';
    private $oauth = null;

    public function __construct($apiKey, $apiSecret, $userId = '')
    {
        $this->apiKey = $apiKey;
        $this->apiSecret = $apiSecret;
        $this->userId = $userId;
    }

    /**
     * Set OAuth handler for authenticated requests
     */
    public function setOAuth($oauth)
    {
        $this->oauth = $oauth;
    }

    /**
     * Check if OAuth is available
     */
    public function hasOAuth()
    {
        return $this->oauth !== null && $this->oauth->isAuthorized();
    }

    /**
     * Set user ID
     */
    public function setUserId($userId)
    {
        $this->userId = $userId;
    }

    /**
     * Make API request (with OAuth if available)
     *
     * @param string $method Flickr API method
     * @param array $params Additional parameters
     * @param bool $useOAuth Force OAuth for this request
     * @return array Response data
     */
    private function request($method, $params = [], $useOAuth = false)
    {
        $params = array_merge([
            'method' => $method,
            'api_key' => $this->apiKey,
            'format' => 'json',
            'nojsoncallback' => 1,
        ], $params);

        // Use OAuth if available and requested
        if (($useOAuth || $this->hasOAuth()) && $this->oauth !== null) {
            return $this->requestWithOAuth($method, $params);
        }

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

        $context = stream_context_create([
            'http' => [
                'timeout' => 30,
                'user_agent' => 'VH_Posting_System/1.0',
            ],
        ]);

        $response = @file_get_contents($url, false, $context);

        if ($response === false) {
            throw new RuntimeException('Failed to connect to Flickr API');
        }

        $data = json_decode($response, true);

        if ($data === null) {
            throw new RuntimeException('Invalid JSON response from Flickr API');
        }

        if (isset($data['stat']) && $data['stat'] === 'fail') {
            throw new RuntimeException('Flickr API error: ' . (isset($data['message']) ? $data['message'] : 'Unknown error'));
        }

        return $data;
    }

    /**
     * Make OAuth-signed API request
     */
    private function requestWithOAuth($method, $params)
    {
        $params['method'] = $method;
        $params['format'] = 'json';
        $params['nojsoncallback'] = 1;

        $oauthParams = $this->oauth->signRequest('GET', $this->baseUrl, $params);
        $allParams = array_merge($params, $oauthParams);

        $url = $this->baseUrl . '?' . http_build_query($allParams);

        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_SSL_VERIFYPEER => true,
        ]);

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

        if ($response === false || $httpCode !== 200) {
            throw new RuntimeException('Failed to connect to Flickr API (OAuth)');
        }

        $data = json_decode($response, true);

        if ($data === null) {
            throw new RuntimeException('Invalid JSON response from Flickr API');
        }

        if (isset($data['stat']) && $data['stat'] === 'fail') {
            throw new RuntimeException('Flickr API error: ' . (isset($data['message']) ? $data['message'] : 'Unknown error'));
        }

        return $data;
    }

    /**
     * Get user's photostream
     *
     * @param int $page Page number
     * @param int $perPage Photos per page (max 500)
     * @return array Photos data
     */
    public function getPhotos($page = 1, $perPage = 50)
    {
        $response = $this->request('flickr.people.getPhotos', [
            'user_id' => $this->userId ? $this->userId : 'me',
            'page' => $page,
            'per_page' => $perPage,
            'extras' => 'url_sq,url_t,url_s,url_m,url_z,url_l,url_k,url_o,description,date_upload,date_taken,owner_name,original_format',
        ]);

        return $this->normalizePhotosResponse(isset($response['photos']) ? $response['photos'] : []);
    }

    /**
     * Get user's photosets (albums)
     *
     * @param int $page Page number
     * @param int $perPage Albums per page
     * @return array Photosets data with pagination info
     */
    public function getPhotosets($page = 1, $perPage = 50)
    {
        $response = $this->request('flickr.photosets.getList', [
            'user_id' => $this->userId,
            'page' => $page,
            'per_page' => $perPage,
            'primary_photo_extras' => 'url_sq,url_t,url_s,url_m',
        ]);

        $photosets = isset($response['photosets']) ? $response['photosets'] : [];

        return [
            'albums' => isset($photosets['photoset']) ? $photosets['photoset'] : [],
            'page' => (int)($photosets['page'] ?? $page),
            'pages' => (int)($photosets['pages'] ?? 1),
            'perpage' => (int)($photosets['perpage'] ?? $perPage),
            'total' => (int)($photosets['total'] ?? 0),
        ];
    }

    /**
     * Get photos from a specific photoset (album)
     *
     * @param string $photosetId Photoset ID
     * @param int $page Page number
     * @param int $perPage Photos per page
     * @return array Photos data
     */
    public function getPhotosetPhotos($photosetId, $page = 1, $perPage = 50)
    {
        $response = $this->request('flickr.photosets.getPhotos', [
            'photoset_id' => $photosetId,
            'user_id' => $this->userId,
            'page' => $page,
            'per_page' => $perPage,
            'extras' => 'url_sq,url_t,url_s,url_m,url_z,url_l,url_k,url_o,description,date_upload,date_taken,original_format,media,path_alias,owner_name',
        ]);

        // Get owner info from photoset response
        $ownername = isset($response['photoset']['ownername']) ? $response['photoset']['ownername'] : '';
        $owner = isset($response['photoset']['owner']) ? $response['photoset']['owner'] : $this->userId;

        return $this->normalizePhotosResponse(isset($response['photoset']) ? $response['photoset'] : [], $ownername, $owner);
    }

    /**
     * Get info about a specific photo
     *
     * @param string $photoId Photo ID
     * @return array Photo info
     */
    public function getPhotoInfo($photoId)
    {
        $response = $this->request('flickr.photos.getInfo', [
            'photo_id' => $photoId,
        ]);

        return isset($response['photo']) ? $response['photo'] : [];
    }

    /**
     * Get available sizes for a photo (uses OAuth if available)
     *
     * @param string $photoId Photo ID
     * @return array Available sizes
     */
    public function getPhotoSizes($photoId)
    {
        // getSizes requires OAuth to return Original for private/restricted photos
        $response = $this->request('flickr.photos.getSizes', [
            'photo_id' => $photoId,
        ], true); // Force OAuth if available

        $sizes = [];
        $sizeList = isset($response['sizes']['size']) ? $response['sizes']['size'] : [];
        foreach ($sizeList as $size) {
            $sizes[$size['label']] = [
                'url' => $size['source'],
                'width' => (int)$size['width'],
                'height' => (int)$size['height'],
            ];
        }

        return $sizes;
    }

    /**
     * Get original URL for a photo (requires OAuth)
     *
     * @param string $photoId Photo ID
     * @return string|null Original URL or null if not available
     */
    public function getOriginalUrl($photoId)
    {
        try {
            $sizes = $this->getPhotoSizes($photoId);

            // Try Original first, then fall back to largest available
            if (isset($sizes['Original'])) {
                return $sizes['Original']['url'];
            }
            if (isset($sizes['Large 2048'])) {
                return $sizes['Large 2048']['url'];
            }
            if (isset($sizes['Large 1600'])) {
                return $sizes['Large 1600']['url'];
            }
            if (isset($sizes['Large'])) {
                return $sizes['Large']['url'];
            }

            return null;
        } catch (Exception $e) {
            return null;
        }
    }

    /**
     * Search user's photos
     *
     * @param string $query Search query
     * @param int $page Page number
     * @param int $perPage Photos per page
     * @return array Photos data
     */
    public function searchPhotos($query, $page = 1, $perPage = 50)
    {
        $response = $this->request('flickr.photos.search', [
            'user_id' => $this->userId ? $this->userId : 'me',
            'text' => $query,
            'page' => $page,
            'per_page' => $perPage,
            'extras' => 'url_sq,url_t,url_s,url_m,url_z,url_l,url_k,url_o,description,date_upload,date_taken,original_format',
        ]);

        return $this->normalizePhotosResponse(isset($response['photos']) ? $response['photos'] : []);
    }

    /**
     * Find user ID by username
     *
     * @param string $username Flickr username
     * @return string User ID
     */
    public function findUserByUsername($username)
    {
        $response = $this->request('flickr.people.findByUsername', [
            'username' => $username,
        ]);

        return isset($response['user']['nsid']) ? $response['user']['nsid'] : '';
    }

    /**
     * Normalize photos response to consistent format
     */
    private function normalizePhotosResponse($response, $defaultOwnerName = '', $defaultOwner = '')
    {
        $photos = [];
        $photoList = isset($response['photo']) ? $response['photo'] : [];

        foreach ($photoList as $photo) {
            $farm = isset($photo['farm']) ? $photo['farm'] : '';
            $server = $photo['server'];
            $id = $photo['id'];
            $originalSecret = isset($photo['originalsecret']) ? $photo['originalsecret'] : $photo['secret'];
            $originalFormat = isset($photo['originalformat']) ? $photo['originalformat'] : 'jpg';

            // Get original URL - ONLY use if API returns it
            // If url_o is not returned, originals are blocked by Flickr privacy settings
            $originalUrl = isset($photo['url_o']) ? $photo['url_o'] : null;

            // Get large 2048 URL (url_k) - from API or construct it
            // This is the best quality available when originals are blocked
            $large2048Url = isset($photo['url_k']) ? $photo['url_k'] : null;

            // Construct large2048 URL if not provided (usually works)
            if (!$large2048Url && $server) {
                $large2048Url = "https://live.staticflickr.com/{$server}/{$id}_{$photo['secret']}_k.jpg";
            }

            // Determine media type (photo or video)
            $mediaType = isset($photo['media']) ? $photo['media'] : 'photo';
            $isVideo = ($mediaType === 'video');

            // Build page URL - use path_alias, ownername, or owner NSID
            $pathAlias = isset($photo['pathalias']) && $photo['pathalias'] ? $photo['pathalias'] : '';
            $ownerName = isset($photo['ownername']) ? $photo['ownername'] : $defaultOwnerName;
            $owner = isset($photo['owner']) ? $photo['owner'] : ($defaultOwner ? $defaultOwner : $this->userId);

            // Prefer path_alias, then ownername, then owner NSID (URL encoded)
            $userPath = $pathAlias ? $pathAlias : ($ownerName ? $ownerName : rawurlencode($owner));
            $pageUrl = "https://www.flickr.com/photos/{$userPath}/{$id}/";

            $photos[] = [
                'id' => $id,
                'secret' => $photo['secret'],
                'server' => $server,
                'farm' => $farm,
                'title' => isset($photo['title']) ? $photo['title'] : 'Untitled',
                'description' => isset($photo['description']['_content']) ? $photo['description']['_content'] : '',
                'date_upload' => isset($photo['dateupload']) ? $photo['dateupload'] : '',
                'date_taken' => isset($photo['datetaken']) ? $photo['datetaken'] : '',
                'original_format' => $originalFormat,
                'original_secret' => $originalSecret,
                'media' => $mediaType,
                'is_video' => $isVideo,
                'urls' => [
                    'square' => isset($photo['url_sq']) ? $photo['url_sq'] : null,
                    'thumbnail' => isset($photo['url_t']) ? $photo['url_t'] : null,
                    'small' => isset($photo['url_s']) ? $photo['url_s'] : null,
                    'medium' => isset($photo['url_m']) ? $photo['url_m'] : null,
                    'medium640' => isset($photo['url_z']) ? $photo['url_z'] : null,
                    'large' => isset($photo['url_l']) ? $photo['url_l'] : null,
                    'large2048' => $large2048Url,
                    'original' => $originalUrl,
                ],
                'page_url' => $pageUrl,
            ];
        }

        return [
            'photos' => $photos,
            'page' => (int)(isset($response['page']) ? $response['page'] : 1),
            'pages' => (int)(isset($response['pages']) ? $response['pages'] : 1),
            'perpage' => (int)(isset($response['perpage']) ? $response['perpage'] : count($photos)),
            'total' => (int)(isset($response['total']) ? $response['total'] : count($photos)),
        ];
    }
}
