package org.pixeldroid.app.profile

import android.content.Intent
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.launch
import org.pixeldroid.app.R
import org.pixeldroid.app.databinding.ActivityProfileBinding
import org.pixeldroid.app.posts.feeds.cachedFeeds.CachedFeedFragment
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
import org.pixeldroid.app.posts.parseHTMLText
import org.pixeldroid.app.utils.BaseActivity
import org.pixeldroid.app.utils.api.PixelfedAPI
import org.pixeldroid.app.utils.api.objects.Account
import org.pixeldroid.app.utils.api.objects.FeedContent
import org.pixeldroid.app.utils.db.entities.UserDatabaseEntity
import org.pixeldroid.app.utils.db.updateUserInfoDb
import org.pixeldroid.app.utils.setProfileImageFromURL
import retrofit2.HttpException
import java.io.IOException

class ProfileActivity : BaseActivity() {

    private lateinit var domain : String
    private lateinit var accountId : String
    private lateinit var binding: ActivityProfileBinding

    private var user: UserDatabaseEntity? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityProfileBinding.inflate(layoutInflater)

        setContentView(binding.root)
        setSupportActionBar(binding.topBar)


        supportActionBar?.setDisplayHomeAsUpEnabled(true)

        user = db.userDao().getActiveUser()

        domain = user?.instance_uri.orEmpty()

        // Set profile according to given account
        val account = intent.getSerializableExtra(Account.ACCOUNT_TAG) as Account?
        accountId = account?.id ?: user!!.user_id

        val tabs = createProfileTabs(account)
        setupTabs(tabs)
        setContent(account)

        binding.profileMotion.setTransitionListener(
            object : MotionLayout.TransitionListener {
                override fun onTransitionStarted(
                    motionLayout: MotionLayout?, startId: Int, endId: Int,
                ) {}

                override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {}

                override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
                    if (currentId == R.id.hideProfile && motionLayout?.startState == R.id.start) {
                        // If the 1st transition has been made go to the second one
                        motionLayout.setTransition(R.id.second)
                    } else if(currentId == R.id.hideProfile && motionLayout?.startState == R.id.hideProfile){
                        motionLayout.setTransition(R.id.first)
                    }
                }

                override fun onTransitionTrigger(
                    motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float,
                ) {}
            }
        )
    }

    private fun createProfileTabs(account: Account?): Array<UncachedFeedFragment<FeedContent>> {

        val profileFeedFragment = ProfileFeedFragment()
        profileFeedFragment.arguments = Bundle().apply {
            putSerializable(Account.ACCOUNT_TAG, account)
            putSerializable(ProfileFeedFragment.PROFILE_GRID, false)
            putSerializable(ProfileFeedFragment.BOOKMARKS, false)
        }

        val profileGridFragment = ProfileFeedFragment()
        profileGridFragment.arguments = Bundle().apply {
            putSerializable(Account.ACCOUNT_TAG, account)
            putSerializable(ProfileFeedFragment.PROFILE_GRID, true)
            putSerializable(ProfileFeedFragment.BOOKMARKS, false)
        }

        val profileCollectionsFragment = ProfileFeedFragment()
        profileCollectionsFragment.arguments = Bundle().apply {
            putSerializable(Account.ACCOUNT_TAG, account)
            putSerializable(ProfileFeedFragment.PROFILE_GRID, true)
            putSerializable(ProfileFeedFragment.BOOKMARKS, false)
            putSerializable(ProfileFeedFragment.COLLECTIONS, true)
        }

        val returnArray: Array<UncachedFeedFragment<FeedContent>> = arrayOf(
            profileGridFragment,
            profileFeedFragment,
            profileCollectionsFragment
        )

        // If we are viewing our own account, show bookmarks
        if(account == null || account.id == user?.user_id) {
            val profileBookmarksFragment = ProfileFeedFragment()
            profileBookmarksFragment.arguments = Bundle().apply {
                putSerializable(Account.ACCOUNT_TAG, account)
                putSerializable(ProfileFeedFragment.PROFILE_GRID, true)
                putSerializable(ProfileFeedFragment.BOOKMARKS, true)
            }
            return returnArray + profileBookmarksFragment
        }
        return returnArray
    }

    private fun setupTabs(
        tabs: Array<UncachedFeedFragment<FeedContent>>,
    ){
        binding.viewPager.adapter = object : FragmentStateAdapter(this) {
            override fun createFragment(position: Int): Fragment {
                return tabs[position]
            }

            override fun getItemCount(): Int {
                return tabs.size
            }
        }
        TabLayoutMediator(binding.profileTabs, binding.viewPager) { tab, position ->
            tab.tabLabelVisibility = TabLayout.TAB_LABEL_VISIBILITY_UNLABELED
            when (position) {
                0 -> {
                    tab.setText(R.string.grid_view)
                    tab.setIcon(R.drawable.grid_on_black_24dp)
                }
                1 -> {
                    tab.setText(R.string.feed_view)
                    tab.setIcon(R.drawable.feed_view)
                }
                2 -> {
                    tab.setText(R.string.collections)
                    tab.setIcon(R.drawable.collections)
                }
                3 -> {
                    tab.setText(R.string.bookmarks)
                    tab.setIcon(R.drawable.bookmark)
                }
            }
        }.attach()

        binding.profileTabs.addOnTabSelectedListener(object : OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab) {}
            override fun onTabUnselected(tab: TabLayout.Tab) {}
            override fun onTabReselected(tab: TabLayout.Tab) {
                tabs[tab.position].onTabReClicked()
            }
        })
    }

    private fun setContent(account: Account?) {
        if(account != null) {
            setViews(account)
        } else {
            supportActionBar?.setTitle(R.string.menu_account)
            lifecycleScope.launchWhenResumed {
                val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
                val myAccount: Account = try {
                    api.verifyCredentials()
                } catch (exception: Exception) {
                    Log.e("ProfileActivity:", exception.toString())
                    Toast.makeText(
                        applicationContext, "Could not get your profile",
                        Toast.LENGTH_SHORT
                    ).show()
                    return@launchWhenResumed
                }

                updateUserInfoDb(db, myAccount)

                setViews(myAccount)
            }
        }

        if(account != null && account.id != user?.user_id) {
            //if we aren't viewing our own account, activate follow button
            activateFollow(account)
        } else {
            //if we *are* viewing our own account, activate the edit button
            activateEditButton()
        }

        // On click open followers list
        binding.nbFollowersTextView.setOnClickListener{ onClickFollowers(account) }
        // On click open followers list
        binding.nbFollowingTextView.setOnClickListener{ onClickFollowing(account) }
    }

    /**
     * Populate profile page with user's data
     */
    private fun setViews(account: Account) {
        val profilePicture = binding.profilePictureImageView
        setProfileImageFromURL(
            View(applicationContext),
            account.anyAvatar(),
            profilePicture
        )

        binding.descriptionTextView.text = parseHTMLText(
                account.note ?: "", emptyList(), apiHolder,
                binding.descriptionTextView.context,
                lifecycleScope
        )
        // This is so that the clicks in the text (eg #, @) work.
        binding.descriptionTextView.movementMethod = LinkMovementMethod.getInstance();

        val displayName = account.getDisplayName()

        binding.accountNameTextView.text = displayName

        supportActionBar?.title = displayName
        if(displayName != "@${account.acct}") {
            supportActionBar?.subtitle = "@${account.acct}"
        }

        binding.nbPostsTextView.text = resources.getQuantityString(
                R.plurals.nb_posts,
                account.statuses_count ?: 0,
                account.statuses_count ?: 0
        )

        binding.nbFollowersTextView.text = resources.getQuantityString(
                R.plurals.nb_followers,
                account.followers_count ?: 0,
                account.followers_count ?: 0
        )

        binding.nbFollowingTextView.text = resources.getQuantityString(
                R.plurals.nb_following,
                account.following_count ?: 0,
                account.following_count ?: 0
        )
    }

    private val editResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
        if (it.resultCode == RESULT_OK) {
            // Profile was edited, reload
            setContent(null)
        }
    }

    private fun onClickEditButton() {
        editResult.launch(Intent(this, EditProfileActivity::class.java))
    }

    private fun onClickFollowers(account: Account?) {
        val intent = Intent(this, FollowsActivity::class.java)
        intent.putExtra(Account.FOLLOWERS_TAG, true)
        intent.putExtra(Account.ACCOUNT_TAG, account)

        ContextCompat.startActivity(this, intent, null)
    }

    private fun onClickFollowing(account: Account?) {
        val intent = Intent(this, FollowsActivity::class.java)
        intent.putExtra(Account.FOLLOWERS_TAG, false)
        intent.putExtra(Account.ACCOUNT_TAG, account)

        ContextCompat.startActivity(this, intent, null)
    }

    private fun activateEditButton() {
        // Edit button redirects to Pixelfed's "edit account" page
        binding.editButton.apply {
            visibility = View.VISIBLE
            setOnClickListener{ onClickEditButton() }
        }
    }

    /**
     * Set up follow button
     */
    private fun activateFollow(account: Account) {
        // Get relationship between the two users (credential and this) and set followButton accordingly
        lifecycleScope.launch {
            try {
                val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
                val relationship = api.checkRelationships(
                    listOf(account.id.orEmpty())
                ).firstOrNull()

                if(relationship != null){
                    if (relationship.following == true || relationship.requested == true) {
                        setOnClickUnfollow(account, relationship.requested == true)
                    } else {
                        setOnClickFollow(account)
                    }
                    binding.followButton.visibility = View.VISIBLE
                }
            } catch (exception: IOException) {
                Log.e("FOLLOW ERROR", exception.toString())
                Toast.makeText(
                    applicationContext, getString(R.string.follow_status_failed),
                    Toast.LENGTH_SHORT
                ).show()
            } catch (exception: HttpException) {
                Toast.makeText(
                    applicationContext, getString(R.string.follow_button_failed),
                    Toast.LENGTH_SHORT
                ).show()
            }
        }
    }

    private fun setOnClickFollow(account: Account) {
        binding.followButton.apply {
            setText(R.string.follow)
            setOnClickListener {
                lifecycleScope.launchWhenResumed {
                    try {
                        val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
                        val rel = api.follow(account.id.orEmpty())
                        if(rel.following == true) setOnClickUnfollow(account, rel.requested == true)
                        else setOnClickFollow(account)
                    } catch (exception: Exception) {
                        Log.e("FOLLOW ERROR", exception.toString())
                        Toast.makeText(
                            applicationContext, getString(R.string.follow_error),
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                }
            }
        }
    }

    private fun setOnClickUnfollow(account: Account, requested: Boolean) {
        binding.followButton.apply {
            if(account.locked == true && requested) {
                setText(R.string.follow_requested)
            } else setText(R.string.unfollow)


            fun unfollow() {
                lifecycleScope.launchWhenResumed {
                    try {
                        val api: PixelfedAPI = apiHolder.api ?: apiHolder.setToCurrentUser()
                        val rel = api.unfollow(account.id.orEmpty())
                        if(rel.following == false && rel.requested == false) setOnClickFollow(account)
                        else setOnClickUnfollow(account, rel.requested == true)
                    } catch (exception: IOException) {
                        Log.e("FOLLOW ERROR", exception.toString())
                        Toast.makeText(
                                applicationContext, getString(R.string.unfollow_error),
                                Toast.LENGTH_SHORT
                        ).show()
                    } catch (exception: HttpException) {
                        Toast.makeText(
                                applicationContext, getString(R.string.unfollow_error),
                                Toast.LENGTH_SHORT
                        ).show()
                    }
                }
            }

            setOnClickListener {
                if(account.locked == true && requested){
                    MaterialAlertDialogBuilder(context)
                            .setMessage(R.string.dialog_message_cancel_follow_request)
                            .setPositiveButton(android.R.string.ok) { _, _ ->
                                unfollow()
                            }
                            .setNegativeButton(android.R.string.cancel){_, _ -> }
                            .show()
                } else unfollow()
            }
        }
    }
}
