<?php

namespace App\Http\Services\Account\Linkedin;

use App\Models\Platform;
use App\Models\SocialAccount;
use App\Traits\AccountManager;
use App\Models\SocialPost;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Arr;

class Account
{

    use AccountManager;


    const BASE_URL = 'https://linkedin.com';
    const API_URL = 'https://api.linkedin.com';


    public static function getApiUrl(string $endpoint, array $params = [], mixed $configuration, bool $isBaseUrl = false): mixed
    {

        $apiUrl = $isBaseUrl ? self::BASE_URL : self::API_URL;

        if (str_starts_with($endpoint, '/'))
            $endpoint = substr($endpoint, 1);

        $v = @$configuration?->app_version ?? null;

        $versionedUrlWithEndpoint = $apiUrl . '/' . (!$isBaseUrl && $v ? ($v . '/') : '') . $endpoint;

        if (count($params))
            $versionedUrlWithEndpoint .= '?' . http_build_query($params);

        return $versionedUrlWithEndpoint;
    }




    /**
     * Summary of getScopes
     * @return array
     */
    public static function getScopes(string $type = 'auth'): array
    {


        switch ($type) {

            case 'auth':
                return ['openid profile email w_member_social'];

            default:

                return ['openid profile email w_member_social'];
        }

    }







    /**
     * Summary of authRedirect
     * @param \App\Models\Platform $platform
     * @return string
     */
    public static function authRedirect(Platform $platform)
    {


        $scopes = collect(self::getScopes())->join(' ');

        $configuration = $platform->configuration;

        return self::getApiUrl('oauth/v2/authorization', [
            'response_type' => 'code',
            'client_id' => $configuration->client_id,
            'redirect_uri' => url('/account/linkedin/callback?medium=' . $platform->slug),

            'scope' => $scopes,
        ], $configuration, true);


    }



    /**
     * Summary of getAccessToken
     * @param string $code
     * @param \App\Models\Platform $platform
     * @return \Illuminate\Http\Client\Response
     */
    public static function getAccessToken(string $code, Platform $platform)
    {

        $configuration = $platform->configuration;

        $apiUrl = self::getApiUrl('oauth/v2/accessToken', [
            'code' => $code,
            'grant_type' => 'authorization_code',
            'client_id' => $configuration->client_id,
            'client_secret' => $configuration->client_secret,
            'redirect_uri' => url('/account/linkedin/callback?medium=' . $platform->slug),
        ], $configuration, true);

        return Http::post($apiUrl);
    }




    /**
     * Summary of refreshAccessToken
     * @param \App\Models\Platform $platform
     * @param string $token
     * @return \Illuminate\Http\Client\Response
     */
    public static function refreshAccessToken(Platform $platform, string $token): \Illuminate\Http\Client\Response
    {

        $configuration = $platform->configuration;

        $apiUrl = self::getApiUrl('oauth/v2/accessToken', [
            'grant_type' => 'refresh_token',
            'refresh_token' => $token,
            'client_id' => $configuration->client_id,
            'client_secret' => $configuration->client_secret,
        ], $configuration, true);

        return Http::asForm()->post($apiUrl);
    }





    /**
     * Summary of getAccount
     * @param string $token
     * @param \App\Models\Platform $platform
     * @return \Illuminate\Http\Client\Response
     */
    public static function getAccount(string $token, Platform $platform)
    {
        $apiUrl = self::getApiUrl('v2/userinfo', [], $platform);
        return Http::withToken($token)->get($apiUrl);
    }





    /**
     * Summary of saveLdAccount
     * @param mixed $user
     * @param string $guard
     * @param \App\Models\Platform $platform
     * @param string $account_type
     * @param string $is_official
     * @param int|string $dbId
     * @return void
     */
    public static function saveLdAccount(
        mixed $user,
        string $guard,
        Platform $platform,
        string $account_type,
        string $is_official,
        string $token,
        mixed $tokenExpireIn,
        int|string $dbId = null
    ) {


        $ld = new self();

        $accountInfo = [

            'id' => $user['sub'],
            'account_id' => $user['sub'],
            'name' => Arr::get($user, 'name', null),
            'avatar' => Arr::get($user, 'picture'),
            'email' => Arr::get($user, 'email'),
            'token' => $token,

            'access_token_expire_at' => now()->addSeconds($tokenExpireIn),
            'refresh_token' => $token,
            'refresh_token_expire_at' => now()->addSeconds($tokenExpireIn),
        ];


        $response = $ld->saveAccount($guard, $platform, $accountInfo, $account_type, $is_official, $dbId);

    }






    public function send(SocialPost $post): array
    {
        $status = false;
        $message = 'Failed to post to LinkedIn';

        try {
            $account = $post->account;
            $token = $account->token;


            $linkedin_id = $account->account_id;

            $platform = @$account?->platform;

            $configuration = $platform->configuration;



            if ($post->files && $post->files->count() > 0) {

                $uploadedMedia = collect([]);

                foreach ($post->files as $file) {

                    $fileURL = imageURL($file, "gallery");

                    $imageContainer = $this->apiClient($token)
                        ->post(self::getApiUrl('rest/images', [
                            'action' => 'initializeUpload'
                        ], $configuration), [
                            "initializeUploadRequest" => ["owner" => "urn:li:person:{$linkedin_id}"]
                        ])
                        ->json('value');


                    $response = $this->apiClient($token)
                        ->attach('file', fopen($fileURL, 'r'))
                        ->post($imageContainer['uploadUrl']);

                    if ($response->created()) {
                        $uploadedMedia->push($imageContainer);
                    }

                }


                $postImages = $uploadedMedia->map(function ($item) {
                    return ['id' => $item['image']];
                });

                $attachMediaObj = ($postImages->count() > 1) ? [
                    "content" => [
                        "multiImage" => [
                            "images" => $postImages->toArray()
                        ]
                    ]
                ] : [
                    "content" => [
                        "media" => [
                            "id" => $postImages->value('id')
                        ]
                    ]
                ];


                $postData = [
                    "author" => "urn:li:person:{$linkedin_id}",
                    "commentary" => $post->content ?? '',
                    "visibility" => 'PUBLIC',
                    "distribution" => [
                        "feedDistribution" => 'MAIN_FEED',
                        "targetEntities" => [],
                        "thirdPartyDistributionChannels" => []
                    ],
                    "lifecycleState" => "PUBLISHED",
                    "isReshareDisabledByAuthor" => false,
                    ...$attachMediaObj
                ];

                $response = $this->apiClient($token)->post(self::getApiUrl('rest/posts', [], $configuration), $postData);


                if ($response->successful()) {

                    return [
                        'status' => true,
                        'response' => translate("Posted Successfully"),
                        'url' => null
                    ];


                }

                return [
                    'status' => false,
                    'response' => @$response->json('message') ?? translate("Failed to post"),
                    'url' => null
                ];


            } else {

                $postData = [
                    "author" => "urn:li:person:{$linkedin_id}",
                    "commentary" => $post->content ?? '',
                    "visibility" => 'PUBLIC',
                    "distribution" => [
                        "feedDistribution" => 'MAIN_FEED',
                        "targetEntities" => [],
                        "thirdPartyDistributionChannels" => []
                    ],
                    "lifecycleState" => "PUBLISHED",
                    "isReshareDisabledByAuthor" => false
                ];


                $response = $this->apiClient($token)->post(self::getApiUrl('rest/posts', [], $configuration), $postData);




                if ($response->successful()) {

                    return [
                        'status' => true,
                        'response' => translate("Posted Successfully"),
                        'url' => null
                    ];

                }

                return [
                    'status' => false,
                    'response' => @$response->json('message') ?? translate("Failed to post"),
                    'url' => null
                ];

            }


        } catch (\Exception $ex) {
            $status = false;
            $message = strip_tags($ex->getMessage());
        }

        return [
            'status' => $status,
            'response' => $message,
            'url' => null
        ];

    }


    /**
     * Upload image to LinkedIn using the new Images API
     * https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/images-api
     *
     * @deprecated This method is not actively used. The send() method handles image uploads directly.
     */
    private function uploadImage(string $imagePath, string $token, int|string $clientId)
    {
        // Initialize upload using new rest/images endpoint
        $initResponse = $this->apiClient($token)
            ->post(self::API_URL . '/rest/images?action=initializeUpload', [
                'initializeUploadRequest' => [
                    'owner' => 'urn:li:person:' . $clientId
                ]
            ]);

        if (!$initResponse->successful()) {
            return $initResponse;
        }

        $uploadData = $initResponse->json('value');
        $uploadUrl = $uploadData['uploadUrl'];

        // Upload the image binary
        $imageResponse = $this->apiClient($token)
            ->attach('file', fopen($imagePath, 'r'))
            ->post($uploadUrl);

        return $imageResponse;
    }






    private function apiClient($token)
    {
        return Http::withHeaders([
            'X-Restli-Protocol-Version' => '2.0.0',
            'LinkedIn-Version' => '202411',
        ])->withToken($token)->retry(1, 3000);
    }

    /**
     * Get account-level insights (connections/followers count)
     * Note: LinkedIn API has limited access to follower data for personal profiles
     * Organization pages have follower stats available with proper permissions
     *
     * @param SocialAccount $account
     * @return array
     */
    public function getAccountInsights(SocialAccount $account): array
    {
        try {
            $token = $account->token ?? $account->access_token ?? null;

            if (!$token) {
                return [
                    'status' => false,
                    'message' => 'Missing access token',
                    'data' => [],
                ];
            }

            // For personal profiles, LinkedIn doesn't expose follower count via API
            // For organization pages, you would use the organizationFollowerStatistics endpoint
            // which requires r_organization_followers permission

            // Try to get organization follower count if this is a company page
            if ($account->account_type === 'organization' || $account->account_type === 'company') {
                $apiUrl = self::API_URL . "/rest/organizationalEntityFollowerStatistics?q=organizationalEntity&organizationalEntity=urn:li:organization:{$account->account_id}";

                $response = $this->apiClient($token)->get($apiUrl);
                $data = $response->json();

                if ($response->successful() && isset($data['elements'][0])) {
                    $followerCount = $data['elements'][0]['followerCounts']['organicFollowerCount'] ?? 0;
                    return [
                        'status' => true,
                        'message' => 'Account insights fetched successfully',
                        'data' => [
                            'followers' => $followerCount,
                        ],
                    ];
                }
            }

            // For personal profiles, return 0 as LinkedIn doesn't expose this
            return [
                'status' => true,
                'message' => 'Follower count not available for personal profiles',
                'data' => [
                    'followers' => 0,
                ],
            ];

        } catch (\Throwable $e) {
            return [
                'status' => false,
                'message' => 'Error fetching account insights: ' . $e->getMessage(),
                'data' => [],
            ];
        }
    }

    /**
     * Get metrics/insights for a LinkedIn post
     * https://learn.microsoft.com/en-us/linkedin/marketing/community-management/members/post-statistics
     *
     * @param SocialPost $post
     * @param SocialAccount $account
     * @return array
     */
    public function getInsight(SocialPost $post, SocialAccount $account): array
    {
        try {
            $token = $account->token ?? $account->access_token ?? null;

            if (!$token || !$post->platform_post_id) {
                return [
                    'status' => false,
                    'message' => 'Missing access token or post ID',
                    'metrics' => [],
                ];
            }

            $metrics = [
                'impressions' => 0,
                'engagements' => 0,
                'reactions' => 0,
                'comments' => 0,
                'shares' => 0,
                'likes' => 0,
                'reach' => 0,
            ];

            // Fetch each metric type from the new API
            // LinkedIn requires separate calls for each metric type
            $metricTypes = ['IMPRESSION', 'REACTION', 'COMMENT', 'RESHARE', 'MEMBERS_REACHED'];
            $postUrn = urlencode($post->platform_post_id);

            foreach ($metricTypes as $metricType) {
                $apiUrl = self::API_URL . "/rest/memberCreatorPostAnalytics?q=entity&entity=(share:{$postUrn})&queryType={$metricType}&aggregation=TOTAL";

                $response = $this->apiClient($account->token)->get($apiUrl);

                if ($response->successful()) {
                    $data = $response->json();
                    $count = $data['elements'][0]['count'] ?? 0;

                    switch ($metricType) {
                        case 'IMPRESSION':
                            $metrics['impressions'] = $count;
                            break;
                        case 'REACTION':
                            $metrics['reactions'] = $count;
                            $metrics['likes'] = $count;
                            break;
                        case 'COMMENT':
                            $metrics['comments'] = $count;
                            break;
                        case 'RESHARE':
                            $metrics['shares'] = $count;
                            break;
                        case 'MEMBERS_REACHED':
                            $metrics['reach'] = $count;
                            break;
                    }
                }
            }

            $metrics['engagements'] = $metrics['reactions'] + $metrics['comments'] + $metrics['shares'];

            return [
                'status' => true,
                'message' => 'Metrics fetched successfully',
                'metrics' => $metrics,
            ];

        } catch (\Throwable $e) {
            return [
                'status' => false,
                'message' => 'Error fetching LinkedIn metrics: ' . $e->getMessage(),
                'metrics' => [],
            ];
        }
    }


}
