import { Page, expect } from '@playwright/test';
import { BasePage } from './BasePage';
import { ChatMessage, BargainingContext, Ware, SuggestedAction } from '../types';

/**
 * Chat Panel Page Object
 * Handles NPC chat interactions embedded in dashboard and other pages
 */
export class ChatPanel extends BasePage {
  // Overall character panel container
  private readonly characterPanel = () => this.page.locator('[data-testid="character-panel"]').first();

  // Chat messages container (scoped inside character panel)
  private readonly chatPanel = () => this.characterPanel().locator('[data-testid="chat-panel"]').first();
  private readonly chatMessages = () => this.chatPanel().locator('[data-testid="npc-message"], [data-testid="user-message"]');
  private readonly userMessages = () => this.chatPanel().locator('[data-testid="user-message"]');
  private readonly npcMessages = () => this.chatPanel().locator('[data-testid="npc-message"]');

  // Input and actions (scoped to outer panel, not messages container)
  private readonly messageInput = () => this.characterPanel().locator('[data-testid="chat-input"]').first();
  private readonly sendButton = () =>
    this.characterPanel().getByRole('button', { name: /^send$/i }).first();
  private readonly resetButton = () =>
    this.characterPanel().getByRole('button', { name: /new opener/i }).first();

  // Character selection buttons (scoped to outer panel)
  private readonly characterSelector = () =>
    this.characterPanel().locator('button').filter({ hasText: /frodo|sam|gandalf/i });
  private readonly characterTabs = () =>
    this.characterPanel().locator('button').filter({ hasText: /frodo|sam|gandalf/i });

  // Suggested actions (scoped to outer panel)
  private readonly suggestedAction = () =>
    this.characterPanel().locator('div').filter({ hasText: /^Suggested action/ }).first();
  private readonly suggestedActionButton = () =>
    this.characterPanel().locator('[data-testid="suggested-action"] button').first();

  constructor(page: Page, baseUrl?: string) {
    super(page, baseUrl);
  }

  /**
   * Get all chat messages as plain text array
   */
  async getMessages(): Promise<string[]> {
    const allMessages = this.chatMessages();
    const messages: string[] = [];
    const count = await allMessages.count();
    for (let i = 0; i < count; i++) {
      const msg = allMessages.nth(i);
      const text = await msg.textContent();
      if (text) {
        messages.push(text.trim());
      }
    }
    return messages;
  }

  /**
   * Get available actions (suggested action buttons)
   */
  async getAvailableActions(): Promise<string[]> {
    const actions: string[] = [];
    const suggestedAction = this.suggestedAction();
    const isVisible = await suggestedAction.isVisible({ timeout: 2000 }).catch(() => false);
    if (isVisible) {
      const text = await suggestedAction.textContent();
      if (text) {
        actions.push(text.trim());
      }
    }
    return actions;
  }

  /**
   * Check if chat panel is loaded and has initial message
   */
  async isLoaded(): Promise<boolean> {
    try {
      const charPanel = this.characterPanel();
      const isVisible = await charPanel.isVisible({ timeout: 3000 });
      if (!isVisible) return false;
      
      const messages = await this.getMessages();
      return messages.length > 0;
    } catch {
      return false;
    }
  }

  /**
   * Open chat panel if it has an open method
   */
  async open(): Promise<void> {
    const charPanel = this.characterPanel();
    try {
      await charPanel.click();
      await this.page.waitForTimeout(300);
    } catch {
      // Panel might already be open
    }
  }

  /**
   * Check if chat panel is visible
   */
  async isVisible(): Promise<boolean> {
    return this.characterPanel().isVisible({ timeout: 3000 }).catch(() => false);
  }

  /**
   * Send a message in chat
   */
  async sendMessage(message: string): Promise<void> {
    // Wait for the send button to be enabled (not loading) before interacting
    await this.sendButton().waitFor({ state: 'visible', timeout: 15000 });
    await this.sendButton().waitFor({ state: 'attached', timeout: 15000 });
    // Poll until button is not disabled (isChatLoading=false)
    await this.page.waitForFunction(
      () => {
        const btn = document.querySelector('[data-testid="send-button"]') as HTMLButtonElement | null;
        return btn !== null && !btn.disabled;
      },
      { timeout: 15000 }
    ).catch(() => {});
    const initialCount = await this.npcMessages().count();
    await this.messageInput().fill(message);
    await this.sendButton().click();
    // Wait for NPC to respond with a new message
    await this.page.waitForFunction(
      ([selector, minCount]) => {
        const els = document.querySelectorAll(selector as string);
        return els.length > (minCount as number);
      },
      ['[data-testid="npc-message"]', initialCount] as [string, number],
      { timeout: 25000 }
    ).catch(() => {
      // Timeout waiting for response - continue anyway
    });
  }

  /**
   * Get last NPC message
   * Polls for message to be rendered
   */
  async getLastNPCMessage(): Promise<string> {
    const messages = this.npcMessages();
    try {
      const count = await messages.count();
      if (count === 0) {
        // Poll for first message to appear
        await this.pollUntil(
          async () => {
            const c = await this.npcMessages().count();
            return c > 0;
          },
          10000,
          500,
        );
      }
    } catch {
      // Continue anyway
    }

    const finalCount = await messages.count();
    if (finalCount === 0) return '';
    const lastMessage = messages.nth(finalCount - 1);
    return lastMessage.textContent().then(t => t || '');
  }

  /**
   * Get last user message
   */
  async getLastUserMessage(): Promise<string> {
    const messages = this.userMessages();
    const count = await messages.count();
    if (count === 0) return '';
    const lastMessage = messages.nth(count - 1);
    return lastMessage.textContent().then(t => t || '');
  }

  /**
   * Get all chat messages
   */
  async getAllMessages(): Promise<Array<{ role: string; text: string }>> {
    const messages: Array<{ role: string; text: string }> = [];
    const allMessages = this.chatMessages();
    const count = await allMessages.count();
    for (let i = 0; i < count; i++) {
      const msg = allMessages.nth(i);
      const text = await msg.textContent();
      // Read the data-testid attribute to determine role
      const testId = await msg.getAttribute('data-testid');
      const role = testId === 'user-message' ? 'user' : 'npc';
      messages.push({
        role,
        text: text || '',
      });
    }
    return messages;
  }

  /**
   * Check if suggested action is visible
   */
  async isSuggestedActionVisible(): Promise<boolean> {
    return this.suggestedAction().isVisible({ timeout: 3000 }).catch(() => false);
  }

  /**
   * Get suggested action text
   */
  async getSuggestedActionText(): Promise<string> {
    return this.suggestedAction().textContent().then(t => t || '');
  }

  /**
   * Click suggested action button
   */
  async clickSuggestedAction(): Promise<void> {
    await this.suggestedActionButton().click();
  }

  /**
   * Select a character to chat with
   * Let the UI handle conversation state - use resetNpcChat if fresh conversation needed
   */
  async selectCharacter(characterName: 'frodo' | 'sam' | 'gandalf'): Promise<void> {
    const tab = this.characterTabs().filter({ hasText: new RegExp(characterName, 'i') }).first();
    await tab.click();
    // Wait for at least one NPC message to appear (opening message / catalog)
    await this.page.waitForFunction(
      () => document.querySelectorAll('[data-testid="npc-message"]').length > 0,
      { timeout: 15000 }
    ).catch(() => {});
    // Wait for send button to become enabled (isChatLoading=false)
    await this.page.waitForFunction(
      () => {
        const btn = document.querySelector('[data-testid="send-button"]') as HTMLButtonElement | null;
        return btn !== null && !btn.disabled;
      },
      { timeout: 10000 }
    ).catch(() => {});
  }

  /**
   * Reset conversation
   */
  async resetConversation(): Promise<void> {
    await this.resetButton().click();
  }

  /**
   * Verify message contains text
   */
  async expectLastNPCMessageContains(text: string | RegExp): Promise<void> {
    const message = await this.getLastNPCMessage();
    if (typeof text === 'string') {
      expect(message).toContain(text);
    } else {
      expect(message).toMatch(text);
    }
  }

  /**
   * Wait for NPC to respond
   * Uses active polling for response message visibility
   */
  async waitForNPCResponse(timeout: number = 15000): Promise<void> {
    try {
      await this.pollForElementVisible(
        this.npcMessages().last(),
        timeout,
        500,
      );
    } catch {
      // Message may not appear, but that's ok
    }
  }
}
