/**
 * ds-base-tool.js
 * Base class for all DSS tool components
 *
 * Enforces DSS coding standards:
 * - Shadow DOM encapsulation
 * - Automatic event listener cleanup via AbortController
 * - Constructable Stylesheets support
 * - Standardized lifecycle methods
 * - Logger utility integration
 *
 * Reference: .knowledge/dss-coding-standards.json
 */

import { logger } from '../../utils/logger.js';

/**
 * Base class for DSS tool components
 * All tool components should extend this class to ensure compliance with DSS standards
 */
export default class DSBaseTool extends HTMLElement {
  constructor() {
    super();

    // WC-001: Shadow DOM Required
    this.attachShadow({ mode: 'open' });

    // EVENT-003: Use AbortController for cleanup
    this._abortController = new AbortController();

    // Track component state
    this._isConnected = false;

    logger.debug(`[${this.constructor.name}] Constructor initialized`);
  }

  /**
   * Standard Web Component lifecycle: called when element is added to DOM
   */
  connectedCallback() {
    this._isConnected = true;
    logger.debug(`[${this.constructor.name}] Connected to DOM`);

    // Render the component
    this.render();

    // Setup event listeners after render
    this.setupEventListeners();
  }

  /**
   * Standard Web Component lifecycle: called when element is removed from DOM
   * Automatically cleans up all event listeners via AbortController
   */
  disconnectedCallback() {
    this._isConnected = false;

    // EVENT-003: Abort all event listeners
    this._abortController.abort();

    // Create new controller for potential re-connection
    this._abortController = new AbortController();

    logger.debug(`[${this.constructor.name}] Disconnected from DOM, listeners cleaned up`);
  }

  /**
   * Centralized event binding with automatic cleanup
   * @param {EventTarget} target - Element to attach listener to
   * @param {string} type - Event type (e.g., 'click', 'mouseover')
   * @param {Function} handler - Event handler function
   * @param {Object} options - Additional addEventListener options
   */
  bindEvent(target, type, handler, options = {}) {
    if (!target || typeof handler !== 'function') {
      logger.warn(`[${this.constructor.name}] Invalid event binding attempt`, { target, type, handler });
      return;
    }

    // Add AbortController signal to options
    const eventOptions = {
      ...options,
      signal: this._abortController.signal
    };

    target.addEventListener(type, handler, eventOptions);

    logger.debug(`[${this.constructor.name}] Event bound: ${type} on`, target);
  }

  /**
   * Event delegation helper for handling multiple elements with data-action attributes
   * @param {string} selector - CSS selector for the container element
   * @param {string} eventType - Event type to listen for
   * @param {Function} handler - Handler function that receives (action, event)
   */
  delegateEvents(selector, eventType, handler) {
    const container = this.shadowRoot.querySelector(selector);
    if (!container) {
      logger.warn(`[${this.constructor.name}] Event delegation container not found: ${selector}`);
      return;
    }

    this.bindEvent(container, eventType, (e) => {
      // Find element with data-action attribute
      const target = e.target.closest('[data-action]');
      if (target) {
        const action = target.dataset.action;
        handler(action, e, target);
      }
    });

    logger.debug(`[${this.constructor.name}] Event delegation setup for ${eventType} on ${selector}`);
  }

  /**
   * Inject CSS styles using Constructable Stylesheets
   * STYLE-002: Use Constructable Stylesheets for shared styles
   * @param {string} cssString - CSS string to inject
   */
  adoptStyles(cssString) {
    try {
      const sheet = new CSSStyleSheet();
      sheet.replaceSync(cssString);

      // Append to existing stylesheets
      this.shadowRoot.adoptedStyleSheets = [
        ...this.shadowRoot.adoptedStyleSheets,
        sheet
      ];

      logger.debug(`[${this.constructor.name}] Styles adopted (${cssString.length} bytes)`);
    } catch (error) {
      logger.error(`[${this.constructor.name}] Failed to adopt styles:`, error);
    }
  }

  /**
   * Set multiple attributes at once
   * @param {Object} attrs - Object with attribute key-value pairs
   */
  setAttributes(attrs) {
    Object.entries(attrs).forEach(([key, value]) => {
      if (value !== null && value !== undefined) {
        this.setAttribute(key, value);
      }
    });
  }

  /**
   * Get attribute with fallback value
   * @param {string} name - Attribute name
   * @param {*} defaultValue - Default value if attribute doesn't exist
   * @returns {string|*} Attribute value or default
   */
  getAttr(name, defaultValue = null) {
    return this.hasAttribute(name) ? this.getAttribute(name) : defaultValue;
  }

  /**
   * Render method - MUST be implemented by subclasses
   * Should set shadowRoot.innerHTML with component template
   */
  render() {
    throw new Error(`${this.constructor.name} must implement render() method`);
  }

  /**
   * Setup event listeners - should be implemented by subclasses
   * Use this.bindEvent() or this.delegateEvents() for automatic cleanup
   */
  setupEventListeners() {
    // Override in subclass if needed
    logger.debug(`[${this.constructor.name}] setupEventListeners() not implemented (optional)`);
  }

  /**
   * Trigger re-render (useful for state changes)
   */
  rerender() {
    if (this._isConnected) {
      // Abort existing listeners before re-render
      this._abortController.abort();
      this._abortController = new AbortController();

      // Re-render and re-setup listeners
      this.render();
      this.setupEventListeners();

      logger.debug(`[${this.constructor.name}] Component re-rendered`);
    }
  }

  /**
   * Helper: Query single element in shadow DOM
   * @param {string} selector - CSS selector
   * @returns {Element|null}
   */
  $(selector) {
    return this.shadowRoot.querySelector(selector);
  }

  /**
   * Helper: Query multiple elements in shadow DOM
   * @param {string} selector - CSS selector
   * @returns {NodeList}
   */
  $$(selector) {
    return this.shadowRoot.querySelectorAll(selector);
  }

  /**
   * Helper: Escape HTML to prevent XSS
   * SECURITY-001: Sanitize user input
   * @param {string} str - String to escape
   * @returns {string} Escaped string
   */
  escapeHtml(str) {
    const div = document.createElement('div');
    div.textContent = str;
    return div.innerHTML;
  }

  /**
   * Helper: Dispatch custom event
   * @param {string} eventName - Event name
   * @param {*} detail - Event detail payload
   * @param {Object} options - Event options
   */
  emit(eventName, detail = null, options = {}) {
    const event = new CustomEvent(eventName, {
      detail,
      bubbles: true,
      composed: true, // Cross shadow DOM boundary
      ...options
    });

    this.dispatchEvent(event);
    logger.debug(`[${this.constructor.name}] Event emitted: ${eventName}`, detail);
  }
}
