package com.example.petstoremobile.fragments;

import android.app.Activity;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.core.view.GravityCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.bumptech.glide.Glide;
import com.example.petstoremobile.R;
import com.example.petstoremobile.adapters.ChatAdapter;
import com.example.petstoremobile.adapters.MessageAdapter;
import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.databinding.FragmentChatBinding;
import com.example.petstoremobile.dtos.ConversationDTO;
import com.example.petstoremobile.dtos.MessageDTO;
import com.example.petstoremobile.models.Chat;
import com.example.petstoremobile.models.Message;
import com.example.petstoremobile.services.ChatNotificationService;
import com.example.petstoremobile.utils.DialogUtils;
import com.example.petstoremobile.utils.FileUtils;
import com.example.petstoremobile.utils.GlideUtils;
import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.UIUtils;
import com.example.petstoremobile.viewmodels.ChatListViewModel;
import com.example.petstoremobile.websocket.StompChatManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import dagger.hilt.android.AndroidEntryPoint;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

@AndroidEntryPoint
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
        StompChatManager.ConversationListener, StompChatManager.ConnectionListener {

    private static final String TAG = "ChatFragment";

    private FragmentChatBinding binding;
    private ChatListViewModel viewModel;

    private ChatAdapter activeChatAdapter;
    private ChatAdapter closedChatAdapter;
    private MessageAdapter messageAdapter;

    private final List<Chat> activeChatList = new ArrayList<>();
    private final List<Chat> closedChatList = new ArrayList<>();
    private final List<Message> messageList = new ArrayList<>();
    private Uri pendingAttachmentUri;

    @Inject TokenManager tokenManager;
    @Inject @Named("baseUrl") String baseUrl;

    private Long activeConversationId;
    private StompChatManager stompChatManager;
    private ActivityResultLauncher<Intent> attachmentLauncher;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewModel = new ViewModelProvider(requireActivity()).get(ChatListViewModel.class);
        attachmentLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                result -> {
                    if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                        Uri uri = result.getData().getData();
                        if (uri != null) showAttachmentPreview(uri);
                    }
                }
        );
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        binding = FragmentChatBinding.inflate(inflater, container, false);

        binding.btnHamburger.setOnClickListener(v -> binding.chatDrawerLayout.openDrawer(GravityCompat.START));

        binding.etMessage.setOnEditorActionListener((v, actionId, event) -> {
            if (actionId == EditorInfo.IME_ACTION_SEND || actionId == EditorInfo.IME_NULL) {
                binding.btnSend.performClick();
                return true;
            }
            return false;
        });

        binding.btnSend.setOnClickListener(v -> {
            if (pendingAttachmentUri != null) sendWithAttachment(pendingAttachmentUri);
            else sendMessage();
        });

        binding.btnAttach.setOnClickListener(v -> selectAttachment());
        binding.btnRemoveAttachment.setOnClickListener(v -> removeAttachment());
        binding.btnCloseChat.setOnClickListener(v -> closeChat());

        setupDrawerToggles();
        setupRecyclerViews();
        observeViewModel();
        loadInitialData();

        return binding.getRoot();
    }

    private void setupDrawerToggles() {
        binding.headerActiveChats.setOnClickListener(v -> {
            if (binding.rvActiveChats.getVisibility() == View.VISIBLE) {
                binding.rvActiveChats.setVisibility(View.GONE);
                binding.ivActiveChevron.setImageResource(android.R.drawable.arrow_down_float);
            } else {
                binding.rvActiveChats.setVisibility(View.VISIBLE);
                binding.ivActiveChevron.setImageResource(android.R.drawable.arrow_up_float);
            }
        });

        binding.headerClosedChats.setOnClickListener(v -> {
            if (binding.rvClosedChats.getVisibility() == View.VISIBLE) {
                binding.rvClosedChats.setVisibility(View.GONE);
                binding.ivClosedChevron.setImageResource(android.R.drawable.arrow_down_float);
            } else {
                binding.rvClosedChats.setVisibility(View.VISIBLE);
                binding.ivClosedChevron.setImageResource(android.R.drawable.arrow_up_float);
            }
        });
    }

    private void setupRecyclerViews() {
        activeChatAdapter = new ChatAdapter(activeChatList, this);
        binding.rvActiveChats.setLayoutManager(new LinearLayoutManager(getContext()));
        binding.rvActiveChats.setAdapter(activeChatAdapter);

        closedChatAdapter = new ChatAdapter(closedChatList, this);
        binding.rvClosedChats.setLayoutManager(new LinearLayoutManager(getContext()));
        binding.rvClosedChats.setAdapter(closedChatAdapter);

        messageAdapter = new MessageAdapter(messageList, null);
        messageAdapter.setBaseUrl(baseUrl);

        messageAdapter.setOnAttachmentClickListener(message -> {
            if (message.getAttachmentMimeType() != null && message.getAttachmentMimeType().startsWith("image/")) {
                showFullScreenImage(message);
            } else {
                downloadFile(message);
            }
        });

        LinearLayoutManager lm = new LinearLayoutManager(getContext());
        lm.setStackFromEnd(true);
        binding.rvMessages.setLayoutManager(lm);
        binding.rvMessages.setItemAnimator(null);
        binding.rvMessages.setAdapter(messageAdapter);
        setConversationActive(false, null);
    }

    private void showFullScreenImage(Message message) {
        if (baseUrl == null || message.getId() == null) return;

        Dialog dialog = new Dialog(requireContext(), android.R.style.Theme_Black_NoTitleBar_Fullscreen);
        dialog.setContentView(R.layout.dialog_full_screen_image);

        ImageView imageView = dialog.findViewById(R.id.ivFullScreen);
        ImageButton closeButton = dialog.findViewById(R.id.btnClose);
        ImageButton downloadButton = dialog.findViewById(R.id.btnDownload);

        String cleanBase = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
        String downloadUrl = cleanBase + "/api/v1/chat/messages/" + message.getId() + "/attachment";

        GlideUtils.loadImageWithToken(requireContext(), imageView, downloadUrl, tokenManager.getToken(), R.drawable.placeholder);

        closeButton.setOnClickListener(v -> dialog.dismiss());
        downloadButton.setOnClickListener(v -> downloadFile(message));
        imageView.setOnClickListener(v -> dialog.dismiss());
        dialog.show();
    }

    private void downloadFile(Message message) {
        if (message.getId() == null) return;

        DialogUtils.showConfirmDialog(requireContext(), "Download Attachment",
                "Do you want to download \"" + message.getAttachmentName() + "\"?", () -> {
            Toast.makeText(requireContext(), "Downloading " + message.getAttachmentName() + "...", Toast.LENGTH_SHORT).show();

            viewModel.downloadAttachment(message.getId()).observe(getViewLifecycleOwner(), resource -> {
                if (resource == null) return;
                setLoading(resource.status == Resource.Status.LOADING);
                if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
                    saveFileToDownloads(resource.data, message.getAttachmentName(), message.getAttachmentMimeType());
                } else if (resource.status == Resource.Status.ERROR) {
                    Toast.makeText(requireContext(), "Download failed: " + resource.message, Toast.LENGTH_SHORT).show();
                }
            });
        });
    }

    private void saveFileToDownloads(ResponseBody body, String fileName, String mimeType) {
        android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
        new Thread(() -> {
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    ContentValues values = new ContentValues();
                    values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
                    values.put(MediaStore.Downloads.MIME_TYPE, mimeType);
                    values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);

                    Uri uri = requireContext().getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
                    if (uri != null) {
                        try (OutputStream outputStream = requireContext().getContentResolver().openOutputStream(uri);
                             InputStream inputStream = body.byteStream()) {
                            byte[] buffer = new byte[4096];
                            int bytesRead;
                            while ((bytesRead = inputStream.read(buffer)) != -1) {
                                outputStream.write(buffer, 0, bytesRead);
                            }
                        }
                        mainHandler.post(() -> Toast.makeText(requireContext(), "File saved to Downloads", Toast.LENGTH_SHORT).show());
                    }
                } else {
                    File downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
                    File file = new File(downloadsDir, fileName);
                    try (OutputStream outputStream = new FileOutputStream(file);
                         InputStream inputStream = body.byteStream()) {
                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                    }
                    mainHandler.post(() -> Toast.makeText(requireContext(), "File saved to Downloads: " + file.getAbsolutePath(), Toast.LENGTH_LONG).show());
                }
                body.close();
            } catch (Exception e) {
                Log.e(TAG, "Error saving file", e);
                mainHandler.post(() -> Toast.makeText(requireContext(), "Error saving file", Toast.LENGTH_SHORT).show());
            }
        }).start();
    }

    private void observeViewModel() {
        viewModel.getActiveChats().observe(getViewLifecycleOwner(), list -> {
            activeChatList.clear();
            activeChatList.addAll(list);
            activeChatAdapter.notifyDataSetChanged();
            updateTitleAndStateIfActive(list);
        });

        viewModel.getClosedChats().observe(getViewLifecycleOwner(), list -> {
            closedChatList.clear();
            closedChatList.addAll(list);
            closedChatAdapter.notifyDataSetChanged();
            updateTitleAndStateIfActive(list);
        });

        viewModel.getMessageList().observe(getViewLifecycleOwner(), list -> {
            int prevSize = messageList.size();
            messageList.clear();
            messageList.addAll(list);
            if (prevSize > 0 && list.size() == prevSize + 1) {
                messageAdapter.notifyItemInserted(list.size() - 1);
            } else {
                messageAdapter.notifyDataSetChanged();
            }
            scrollToBottom();
        });

        viewModel.getIsLoading().observe(getViewLifecycleOwner(), this::setLoading);
    }

    private void setLoading(boolean loading) {
        if (binding != null && binding.progressBar != null) {
            binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
        }
    }

    private void updateTitleAndStateIfActive(List<Chat> list) {
        if (activeConversationId != null) {
            for (Chat chat : list) {
                if (chat.getChatId().equals(String.valueOf(activeConversationId))) {
                    binding.tvChatTitle.setText(chat.getCustomerName());
                    setConversationActive(true, chat.getStatus());
                    break;
                }
            }
        }
    }

    private void loadInitialData() {
        String token = tokenManager.getToken();
        Long currentUserId = tokenManager.getUserId();
        String role = tokenManager.getRole();

        messageAdapter.setCurrentUserId(currentUserId);
        messageAdapter.setToken(token);

        if (token != null) {
            stompChatManager = new StompChatManager(token, role, baseUrl);
            stompChatManager.setMessageListener(this);
            stompChatManager.setConversationListener(this);
            stompChatManager.setConnectionListener(this);
            stompChatManager.connect();
        }

        if (getArguments() != null && getArguments().containsKey("conversation_id")) {
            activeConversationId = getArguments().getLong("conversation_id");
        } else if (getActivity() != null && getActivity().getIntent().hasExtra("conversation_id")) {
            activeConversationId = getActivity().getIntent().getLongExtra("conversation_id", -1);
            getActivity().getIntent().removeExtra("conversation_id");
        } else {
            activeConversationId = viewModel.getLastActiveConversationId();
        }

        viewModel.loadCustomers();

        if (activeConversationId != null) {
            if (stompChatManager != null) stompChatManager.subscribeToConversation(activeConversationId);
            viewModel.loadMessageHistory(activeConversationId);
        } else {
            setConversationActive(false, null);
        }
    }

    @Override
    public void onChatClick(Chat chat) {
        activeConversationId = Long.parseLong(chat.getChatId());
        viewModel.setLastActiveConversationId(activeConversationId);

        setConversationActive(true, chat.getStatus());
        binding.tvChatTitle.setText(chat.getCustomerName());
        binding.chatDrawerLayout.closeDrawer(GravityCompat.START);

        messageAdapter.setStaffId(chat.getStaffId());

        if (stompChatManager != null) stompChatManager.subscribeToConversation(activeConversationId);
        viewModel.loadMessageHistory(activeConversationId);
    }

    private void closeChat() {
        if (activeConversationId == null) return;

        DialogUtils.showConfirmDialog(requireContext(), "Close Chat",
                "Are you sure you want to close this chat? This will notify the customer.", () -> {
            viewModel.sendMessage(activeConversationId, "The Chat has been closed").observe(getViewLifecycleOwner(), resource -> {
                if (resource != null && resource.status == Resource.Status.SUCCESS) {
                    viewModel.addMessageLocally(resource.data);
                    
                    viewModel.closeConversation(activeConversationId).observe(getViewLifecycleOwner(), statusResource -> {
                        if (statusResource == null) return;
                        setLoading(statusResource.status == Resource.Status.LOADING);
                        if (statusResource.status == Resource.Status.SUCCESS) {
                            viewModel.loadConversations();
                            setConversationActive(true, "CLOSED");
                        } else if (statusResource.status == Resource.Status.ERROR) {
                            Toast.makeText(requireContext(), "Failed to close chat: " + statusResource.message, Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            });
        });
    }

    private void sendMessage() {
        if (activeConversationId == null) return;
        String text = binding.etMessage.getText().toString().trim();
        if (text.isEmpty()) return;

        binding.etMessage.setText("");
        viewModel.sendMessage(activeConversationId, text).observe(getViewLifecycleOwner(), resource -> {
            if (resource == null) return;
            setLoading(resource.status == Resource.Status.LOADING);
            if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
                viewModel.addMessageLocally(resource.data);
                viewModel.loadConversations();
            }
        });
    }

    private void selectAttachment() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        attachmentLauncher.launch(intent);
    }

    private void showAttachmentPreview(Uri uri) {
        pendingAttachmentUri = uri;
        binding.layoutAttachmentPreview.setVisibility(View.VISIBLE);
        String mimeType = requireContext().getContentResolver().getType(uri);
        binding.tvPreviewName.setText(FileUtils.getFileName(requireContext(), uri));
        if (mimeType != null && mimeType.startsWith("image/")) {
            binding.ivPreview.setVisibility(View.VISIBLE);
            Glide.with(this).load(uri).into(binding.ivPreview);
        } else {
            binding.ivPreview.setVisibility(View.GONE);
        }
    }

    private void removeAttachment() {
        pendingAttachmentUri = null;
        binding.layoutAttachmentPreview.setVisibility(View.GONE);
    }

    private void sendWithAttachment(Uri uri) {
        if (activeConversationId == null) return;

        File file = FileUtils.getFileFromUri(requireContext(), uri);
        if (file == null) {
            Toast.makeText(requireContext(), "Failed to prepare file", Toast.LENGTH_SHORT).show();
            return;
        }

        String text = binding.etMessage.getText().toString().trim();

        MultipartBody.Part contentPart = text.isEmpty()
                ? null
                : MultipartBody.Part.createFormData("content", text);

        String mimeType = requireContext().getContentResolver().getType(uri);
        if (mimeType == null) mimeType = "application/octet-stream";

        RequestBody filePartBody = RequestBody.create(file, MediaType.parse(mimeType));
        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), filePartBody);

        binding.etMessage.setText("");
        removeAttachment();

        viewModel.sendMessageWithAttachment(activeConversationId, contentPart, filePart).observe(getViewLifecycleOwner(), resource -> {
            if (resource == null) return;
            setLoading(resource.status == Resource.Status.LOADING);
            if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
                viewModel.addMessageLocally(resource.data);
                viewModel.loadConversations();
            } else if (resource.status == Resource.Status.ERROR) {
                Toast.makeText(requireContext(), "Failed to send attachment: " + resource.message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onMessageReceived(MessageDTO dto) {
        requireActivity().runOnUiThread(() -> {
            if (activeConversationId != null && activeConversationId.equals(dto.getConversationId())) {
                viewModel.addMessageLocally(dto);
            }
            viewModel.updateConversationLocally(new ConversationDTO(dto.getConversationId(), 0L, 0L, dto.getContent(), ""));
        });
    }

    @Override
    public void onConversationUpdated(ConversationDTO dto) {
        requireActivity().runOnUiThread(() -> {
            viewModel.updateConversationLocally(dto);
            if (activeConversationId != null && activeConversationId.equals(dto.getId())) {
                setConversationActive(true, dto.getStatus());
                binding.tvChatTitle.setText(viewModel.getCustomerName(dto.getCustomerId()));
            }
        });
    }

    @Override
    public void onSocketOpened() {
        if (!isAdded()) return;
        requireActivity().runOnUiThread(() -> {
            viewModel.loadConversations();
            if (activeConversationId != null) viewModel.loadMessageHistory(activeConversationId);
        });
    }

    @Override
    public void onSocketClosed() {
        if (!isAdded()) return;
        requireActivity().runOnUiThread(viewModel::loadConversations);
    }

    @Override
    public void onSocketError() {
        if (!isAdded()) return;
        requireActivity().runOnUiThread(() -> {
            viewModel.loadConversations();
            if (activeConversationId != null) viewModel.loadMessageHistory(activeConversationId);
        });
    }

    private void scrollToBottom() {
        if (!messageList.isEmpty()) {
            binding.rvMessages.post(() ->
                    binding.rvMessages.smoothScrollToPosition(messageList.size() - 1));
        }
    }

    private void setConversationActive(boolean active, String status) {
        boolean isClosed = "CLOSED".equalsIgnoreCase(status);
        UIUtils.setViewsEnabled(active && !isClosed, binding.btnSend, binding.etMessage, binding.btnAttach);
        binding.btnCloseChat.setVisibility(active && !isClosed ? View.VISIBLE : View.GONE);
        
        if (!active) {
            activeConversationId = null;
            ChatNotificationService.activeConversationIdInUi = null;
            removeAttachment();
            if (binding != null && binding.tvChatTitle != null) binding.tvChatTitle.setText("Customer Chat");
            if (stompChatManager != null) stompChatManager.clearConversationSubscription();
            messageList.clear();
            messageAdapter.notifyDataSetChanged();
            binding.etMessage.setText("");
            binding.etMessage.setHint("Select a chat to start messaging");
        } else {
            binding.etMessage.setHint(isClosed ? "This chat is closed" : "Type a message...");
            ChatNotificationService.activeConversationIdInUi = activeConversationId;
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
        ChatNotificationService.activeConversationIdInUi = null;
        if (stompChatManager != null) stompChatManager.disconnect();
    }
}
