"""
Dynamics 365 GUI Integration Module
Tkinter widgets matching Salesforce tab interface
FIXED: Uses AsyncTkBridge pattern for proper async handling
"""

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import asyncio
from typing import List, Optional, Callable
import pandas as pd
import logging
import webbrowser

# Import AsyncTkBridge from shared module
try:
    from ..shared.async_tk_bridge import AsyncTkBridge
except ImportError:
    # Define inline if shared module doesn't exist yet
    class AsyncTkBridge:
        """Bridge between Tkinter and asyncio"""

        def __init__(self, root: tk.Tk):
            self.root = root
            self.loop = asyncio.new_event_loop()
            self._running = False

        def run_coroutine(self, coro):
            """Run a coroutine and schedule GUI updates"""
            if not self._running:
                asyncio.set_event_loop(self.loop)
                self._running = True

            future = asyncio.run_coroutine_threadsafe(coro, self.loop)
            self.root.after(100, self._check_future, future)
            return future

        def _check_future(self, future):
            """Check if future is done and schedule next check if not"""
            if not future.done():
                self.root.after(100, self._check_future, future)
            else:
                try:
                    future.result()
                except Exception as e:
                    logger.error(f"Async operation failed: {e}")


logger = logging.getLogger(__name__)


class DynamicsConnectionDialog(tk.Toplevel):
    """
    Connection dialog for Dynamics 365
    Clones SalesforceConnectionDialog interface
    """

    def __init__(
        self,
        parent,
        callback: Callable = None,
        async_bridge: Optional[AsyncTkBridge] = None,
    ):
        super().__init__(parent)
        self.parent = parent
        self.callback = callback
        self.async_bridge = async_bridge
        self.result = None

        self.title("Connect to Dynamics 365")
        self.geometry("500x400")
        self.resizable(False, False)

        # Center the dialog
        self.transient(parent)
        self.grab_set()

        self._create_widgets()
        self._center_window()

    def _create_widgets(self):
        """Create dialog widgets"""
        # Main frame
        main_frame = ttk.Frame(self, padding="20")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # Title
        title_label = ttk.Label(
            main_frame, text="Dynamics 365 Connection", font=("Arial", 14, "bold")
        )
        title_label.grid(row=0, column=0, columnspan=2, pady=(0, 20))

        # Client ID
        ttk.Label(main_frame, text="Client ID:").grid(
            row=1, column=0, sticky=tk.W, pady=5
        )
        self.client_id_var = tk.StringVar()
        self.client_id_entry = ttk.Entry(
            main_frame, textvariable=self.client_id_var, width=40
        )
        self.client_id_entry.grid(row=1, column=1, pady=5)

        # Tenant ID
        ttk.Label(main_frame, text="Tenant ID:").grid(
            row=2, column=0, sticky=tk.W, pady=5
        )
        self.tenant_id_var = tk.StringVar()
        self.tenant_id_entry = ttk.Entry(
            main_frame, textvariable=self.tenant_id_var, width=40
        )
        self.tenant_id_entry.grid(row=2, column=1, pady=5)

        # Organization URL
        ttk.Label(main_frame, text="Org URL:").grid(
            row=3, column=0, sticky=tk.W, pady=5
        )
        self.org_url_var = tk.StringVar()
        self.org_url_entry = ttk.Entry(
            main_frame, textvariable=self.org_url_var, width=40
        )
        self.org_url_entry.grid(row=3, column=1, pady=5)

        # Placeholder hint
        hint_label = ttk.Label(
            main_frame,
            text="e.g., https://yourorg.crm.dynamics.com",
            font=("Arial", 9),
            foreground="gray",
        )
        hint_label.grid(row=4, column=1, sticky=tk.W)

        # Sandbox checkbox
        self.is_sandbox_var = tk.BooleanVar()
        self.sandbox_check = ttk.Checkbutton(
            main_frame,
            text="Connect to Sandbox/Trial Environment",
            variable=self.is_sandbox_var,
        )
        self.sandbox_check.grid(row=5, column=0, columnspan=2, pady=(10, 5))

        # Help link
        help_link = ttk.Label(
            main_frame,
            text="How to register an Azure App?",
            foreground="blue",
            cursor="hand2",
        )
        help_link.grid(row=6, column=0, columnspan=2, pady=10)
        help_link.bind("<Button-1>", self._open_help_link)

        # Button frame
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=7, column=0, columnspan=2, pady=(20, 0))

        # Connect button
        self.connect_btn = ttk.Button(
            button_frame, text="Connect", command=self._on_connect
        )
        self.connect_btn.pack(side=tk.LEFT, padx=5)

        # Cancel button
        ttk.Button(button_frame, text="Cancel", command=self.destroy).pack(side=tk.LEFT)

        # Status label
        self.status_label = ttk.Label(main_frame, text="", foreground="red")
        self.status_label.grid(row=8, column=0, columnspan=2, pady=(10, 0))

        # Focus on first entry
        self.client_id_entry.focus()

    def _center_window(self):
        """Center dialog on parent window"""
        self.update_idletasks()
        x = (
            self.parent.winfo_x()
            + (self.parent.winfo_width() // 2)
            - (self.winfo_width() // 2)
        )
        y = (
            self.parent.winfo_y()
            + (self.parent.winfo_height() // 2)
            - (self.winfo_height() // 2)
        )
        self.geometry(f"+{x}+{y}")

    def _open_help_link(self, event):
        """Open help documentation"""
        webbrowser.open(
            "https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app"
        )

    def _on_connect(self):
        """Handle connect button click"""
        # Validate inputs
        client_id = self.client_id_var.get().strip()
        tenant_id = self.tenant_id_var.get().strip()
        org_url = self.org_url_var.get().strip()

        if not client_id:
            self.status_label.config(text="Client ID is required")
            return

        if not tenant_id:
            self.status_label.config(text="Tenant ID is required")
            return

        if not org_url:
            self.status_label.config(text="Organization URL is required")
            return

        # Disable controls during connection
        self.connect_btn.config(state="disabled")
        self.status_label.config(text="Connecting...", foreground="blue")

        # Store result
        self.result = {
            "client_id": client_id,
            "tenant_id": tenant_id,
            "resource": org_url,
            "is_sandbox": self.is_sandbox_var.get(),
        }

        # Call callback if provided
        if self.callback:
            self.callback(self.result)

        self.destroy()


class EntitySelector(ttk.Frame):
    """
    Entity selector widget
    Maps from SalesforceObjectSelector
    """

    def __init__(
        self,
        parent,
        integration,
        async_bridge: Optional[AsyncTkBridge] = None,
        **kwargs,
    ):
        super().__init__(parent, **kwargs)

        self.integration = integration
        self.async_bridge = async_bridge or AsyncTkBridge(parent.winfo_toplevel())
        self.selected_entity = None
        self._entities_cache = None

        self._create_widgets()

    def _create_widgets(self):
        """Create selector widgets"""
        # Search frame
        search_frame = ttk.Frame(self)
        search_frame.pack(fill=tk.X, padx=5, pady=5)

        ttk.Label(search_frame, text="Search:").pack(side=tk.LEFT, padx=(0, 5))

        self.search_var = tk.StringVar()
        self.search_var.trace("w", self._on_search_change)
        self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var)
        self.search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)

        # Custom only checkbox
        self.custom_only_var = tk.BooleanVar()
        self.custom_check = ttk.Checkbutton(
            search_frame,
            text="Custom Only",
            variable=self.custom_only_var,
            command=self._refresh_entities,
        )
        self.custom_check.pack(side=tk.LEFT, padx=(10, 0))

        # Entity listbox with scrollbar
        list_frame = ttk.Frame(self)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        scrollbar = ttk.Scrollbar(list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.entity_listbox = tk.Listbox(list_frame, yscrollcommand=scrollbar.set)
        self.entity_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.entity_listbox.yview)

        self.entity_listbox.bind("<<ListboxSelect>>", self._on_entity_select)

        # Details frame
        details_frame = ttk.LabelFrame(self, text="Entity Details", padding=10)
        details_frame.pack(fill=tk.X, padx=5, pady=5)

        self.details_text = tk.Text(
            details_frame, height=4, wrap=tk.WORD, state=tk.DISABLED
        )
        self.details_text.pack(fill=tk.X)

        # Load entities button
        self.load_btn = ttk.Button(
            self, text="Load Entities", command=self._load_entities
        )
        self.load_btn.pack(pady=5)

    def _load_entities(self):
        """Load entities from Dynamics"""
        self.load_btn.config(state="disabled", text="Loading...")

        # Run async operation using bridge
        self.async_bridge.run_coroutine(self._load_entities_async())

    async def _load_entities_async(self):
        """Async entity loading"""
        try:
            entities = await self.integration.get_entities(
                custom_only=self.custom_only_var.get()
            )
            self._entities_cache = entities
            self._refresh_entity_list()

        except Exception as e:
            messagebox.showerror("Error", f"Failed to load entities: {str(e)}")
        finally:
            self.load_btn.config(state="normal", text="Load Entities")

    def _refresh_entities(self):
        """Refresh entity list based on filters"""
        if self._entities_cache:
            self._refresh_entity_list()
        else:
            self._load_entities()

    def _refresh_entity_list(self):
        """Refresh the entity listbox"""
        self.entity_listbox.delete(0, tk.END)

        if not self._entities_cache:
            return

        search_term = self.search_var.get().lower()
        custom_only = self.custom_only_var.get()

        for entity in self._entities_cache:
            if custom_only and not entity.is_custom:
                continue

            display_name = f"{entity.display_name} ({entity.logical_name})"
            if not search_term or search_term in display_name.lower():
                self.entity_listbox.insert(tk.END, display_name)

    def _on_search_change(self, *args):
        """Handle search text change"""
        self._refresh_entity_list()

    def _on_entity_select(self, event):
        """Handle entity selection"""
        selection = self.entity_listbox.curselection()
        if not selection:
            return

        # Get selected entity
        display_text = self.entity_listbox.get(selection[0])
        logical_name = display_text.split("(")[1].rstrip(")")

        # Find entity in cache
        for entity in self._entities_cache:
            if entity.logical_name == logical_name:
                self.selected_entity = entity
                self._show_entity_details(entity)
                break

    def _show_entity_details(self, entity):
        """Show entity details"""
        self.details_text.config(state=tk.NORMAL)
        self.details_text.delete("1.0", tk.END)

        details = f"Logical Name: {entity.logical_name}\n"
        details += f"Display Name: {entity.display_name}\n"
        details += f"Type: {'Custom' if entity.is_custom else 'Standard'}\n"
        if hasattr(entity, "entity_set_name") and entity.entity_set_name:
            details += f"Entity Set: {entity.entity_set_name}\n"

        self.details_text.insert("1.0", details)
        self.details_text.config(state=tk.DISABLED)

    def get_selected_entity(self) -> Optional[str]:
        """Get selected entity logical name"""
        return self.selected_entity.logical_name if self.selected_entity else None


class WorkflowDialog(tk.Toplevel):
    """
    Workflow execution dialog
    Supports same workflows as Salesforce version
    """

    def __init__(
        self,
        parent,
        workflow_engine,
        workflow_type: str,
        async_bridge: Optional[AsyncTkBridge] = None,
    ):
        super().__init__(parent)

        self.workflow_engine = workflow_engine
        self.workflow_type = workflow_type
        self.async_bridge = async_bridge or AsyncTkBridge(parent)
        self.params = {}

        self.title(f"Execute {workflow_type.replace('_', ' ').title()} Workflow")
        self.geometry("600x500")

        self._create_widgets()
        self._center_window()

    def _create_widgets(self):
        """Create workflow-specific widgets"""
        # Main notebook for parameters
        self.notebook = ttk.Notebook(self, padding=10)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Add workflow-specific tabs
        if self.workflow_type == "entity_dedupe":
            self._create_dedupe_tab()
        elif self.workflow_type == "contact_account_association":
            self._create_association_tab()
        elif self.workflow_type == "tag_propagation":
            self._create_tag_tab()

        # Progress frame
        progress_frame = ttk.Frame(self)
        progress_frame.pack(fill=tk.X, padx=10, pady=(0, 10))

        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(progress_frame, variable=self.progress_var)
        self.progress_bar.pack(fill=tk.X, pady=5)

        self.status_label = ttk.Label(progress_frame, text="Ready to execute")
        self.status_label.pack()

        # Button frame
        button_frame = ttk.Frame(self)
        button_frame.pack(pady=10)

        self.execute_btn = ttk.Button(
            button_frame, text="Execute", command=self._execute_workflow
        )
        self.execute_btn.pack(side=tk.LEFT, padx=5)

        ttk.Button(button_frame, text="Close", command=self.destroy).pack(side=tk.LEFT)

    def _create_dedupe_tab(self):
        """Create deduplication parameters tab"""
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="Deduplication Parameters")

        # Entity selection
        ttk.Label(tab, text="Entity:").grid(
            row=0, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.entity_var = tk.StringVar()
        entity_combo = ttk.Combobox(
            tab, textvariable=self.entity_var, values=["contact", "account", "lead"]
        )
        entity_combo.grid(row=0, column=1, padx=5, pady=5)
        entity_combo.current(0)

        # Merge strategy
        ttk.Label(tab, text="Merge Strategy:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.strategy_var = tk.StringVar()
        strategy_combo = ttk.Combobox(
            tab,
            textvariable=self.strategy_var,
            values=["most_complete", "newest", "oldest"],
        )
        strategy_combo.grid(row=1, column=1, padx=5, pady=5)
        strategy_combo.current(0)

        # File selection
        ttk.Label(tab, text="Duplicate Groups File:").grid(
            row=2, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.file_var = tk.StringVar()
        ttk.Entry(tab, textvariable=self.file_var, width=40).grid(
            row=2, column=1, padx=5, pady=5
        )
        ttk.Button(tab, text="Browse", command=self._select_file).grid(
            row=2, column=2, padx=5
        )

    def _create_association_tab(self):
        """Create association parameters tab"""
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="Association Parameters")

        # Relationship type
        ttk.Label(tab, text="Relationship:").grid(
            row=0, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.relationship_var = tk.StringVar()
        rel_combo = ttk.Combobox(
            tab,
            textvariable=self.relationship_var,
            values=["parentcustomerid", "accountid"],
        )
        rel_combo.grid(row=0, column=1, padx=5, pady=5)
        rel_combo.current(0)

        # File selection
        ttk.Label(tab, text="Associations File:").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.file_var = tk.StringVar()
        ttk.Entry(tab, textvariable=self.file_var, width=40).grid(
            row=1, column=1, padx=5, pady=5
        )
        ttk.Button(tab, text="Browse", command=self._select_file).grid(
            row=1, column=2, padx=5
        )

    def _create_tag_tab(self):
        """Create tag propagation parameters tab"""
        tab = ttk.Frame(self.notebook)
        self.notebook.add(tab, text="Tag Parameters")

        # Entity selection
        ttk.Label(tab, text="Entity:").grid(
            row=0, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.entity_var = tk.StringVar()
        ttk.Entry(tab, textvariable=self.entity_var).grid(
            row=0, column=1, padx=5, pady=5
        )

        # Tags to apply
        ttk.Label(tab, text="Tags (JSON):").grid(
            row=1, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.tags_text = tk.Text(tab, height=5, width=40)
        self.tags_text.grid(row=1, column=1, padx=5, pady=5)
        self.tags_text.insert("1.0", '{"icp_flag": true}')

        # Records file
        ttk.Label(tab, text="Records File:").grid(
            row=2, column=0, sticky=tk.W, padx=5, pady=5
        )
        self.file_var = tk.StringVar()
        ttk.Entry(tab, textvariable=self.file_var, width=40).grid(
            row=2, column=1, padx=5, pady=5
        )
        ttk.Button(tab, text="Browse", command=self._select_file).grid(
            row=2, column=2, padx=5
        )

    def _select_file(self):
        """Select input file"""
        filename = filedialog.askopenfilename(
            title="Select Input File",
            filetypes=[("CSV files", "*.csv"), ("All files", "*.*")],
        )
        if filename:
            self.file_var.set(filename)

    def _center_window(self):
        """Center dialog on screen"""
        self.update_idletasks()
        x = (self.winfo_screenwidth() // 2) - (self.winfo_width() // 2)
        y = (self.winfo_screenheight() // 2) - (self.winfo_height() // 2)
        self.geometry(f"+{x}+{y}")

    def _execute_workflow(self):
        """Execute the workflow"""
        # Gather parameters based on workflow type
        if self.workflow_type == "entity_dedupe":
            self.params = {
                "entity": self.entity_var.get(),
                "merge_strategy": self.strategy_var.get(),
                "duplicate_groups": self._load_duplicate_groups(),
            }
        elif self.workflow_type == "contact_account_association":
            self.params = {
                "relationship_type": self.relationship_var.get(),
                "associations": self._load_associations(),
            }
        elif self.workflow_type == "tag_propagation":
            import json

            self.params = {
                "entity": self.entity_var.get(),
                "updates": json.loads(self.tags_text.get("1.0", tk.END)),
                "records": self._load_record_ids(),
            }

        # Disable execute button
        self.execute_btn.config(state="disabled")

        # Run workflow asynchronously using bridge
        self.async_bridge.run_coroutine(self._run_workflow_async())

    async def _run_workflow_async(self):
        """Run workflow asynchronously"""
        try:
            result = await self.workflow_engine.execute_workflow(
                workflow_type=self.workflow_type,
                params=self.params,
                progress_cb=self._update_progress,
            )

            # Show results
            self._show_results(result)

        except Exception as e:
            messagebox.showerror("Workflow Error", str(e))
        finally:
            self.execute_btn.config(state="normal")

    def _update_progress(self, progress: float, message: str):
        """Update progress bar and status"""
        self.progress_var.set(progress * 100)
        self.status_label.config(text=message)
        self.update_idletasks()

    def _show_results(self, result):
        """Show workflow results"""
        message = f"Workflow: {result.workflow_name}\n"
        message += f"Status: {result.status}\n"
        message += f"Total Processed: {result.total_processed}\n"
        message += f"Successful: {result.successful}\n"
        message += f"Failed: {result.failed}\n"
        message += f"Execution Time: {result.execution_time:.2f} seconds"

        if result.errors:
            message += "\n\nFirst 5 errors:\n"
            for error in result.errors[:5]:
                message += f"- {error}\n"

        messagebox.showinfo("Workflow Results", message)

    def _load_duplicate_groups(self) -> List[List[str]]:
        """Load duplicate groups from file"""
        if not self.file_var.get():
            return []

        try:
            df = pd.read_csv(self.file_var.get())
            # Assume CSV has columns: group_id, record_id
            groups = df.groupby("group_id")["record_id"].apply(list).tolist()
            return groups
        except Exception as e:
            logger.error(f"Failed to load duplicate groups: {e}")
            return []

    def _load_associations(self) -> List[tuple]:
        """Load associations from file"""
        if not self.file_var.get():
            return []

        try:
            df = pd.read_csv(self.file_var.get())
            # Assume CSV has columns: contact_id, account_id
            return list(
                df[["contact_id", "account_id"]].itertuples(index=False, name=None)
            )
        except Exception as e:
            logger.error(f"Failed to load associations: {e}")
            return []

    def _load_record_ids(self) -> List[str]:
        """Load record IDs from file"""
        if not self.file_var.get():
            return []

        try:
            df = pd.read_csv(self.file_var.get())
            # Assume CSV has column: record_id
            return df["record_id"].tolist()
        except Exception as e:
            logger.error(f"Failed to load record IDs: {e}")
            return []
