<?php
/**
 * TagManager — Product tag CRUD, merge, and product association.
 *
 * @package TitanCart\Modules\Products
 */

namespace TitanCart\Modules\Products;

use TitanCart\DB\Database;

defined( 'ABSPATH' ) || exit;

class TagManager {

    private Database $db;

    public function __construct() {
        $this->db = titancart_db();
    }

    // ══════════════════════════════════════════════════════════════════════════
    // LIST
    // ══════════════════════════════════════════════════════════════════════════

    public function list_tags( array $args = [] ): array {
        $page     = max( 1, (int) ( $args['page']     ?? 1 ) );
        $per_page = min( 200, max( 1, (int) ( $args['per_page'] ?? 50 ) ) );
        $offset   = ( $page - 1 ) * $per_page;
        $search   = $args['search'] ?? '';
        $sort     = in_array( $args['sort'] ?? '', [ 'name', 'product_count', 'created_at' ], true )
                        ? $args['sort'] : 'name';
        $order    = strtoupper( $args['order'] ?? 'ASC' ) === 'DESC' ? 'DESC' : 'ASC';

        $where  = [];
        $params = [];

        if ( $search ) {
            $like     = '%' . $this->db->escape_like( $search ) . '%';
            $where[]  = 'name LIKE ?s';
            $params[] = $like;
        }

        $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';

        $total = (int) $this->db->getField(
            "SELECT COUNT(*) FROM {$this->db->table('tags')} {$where_sql}",
            ...$params
        );

        $list_params   = $params;
        $list_params[] = $per_page;
        $list_params[] = $offset;

        $rows = $this->db->getArray(
            "SELECT * FROM {$this->db->table('tags')}
             {$where_sql}
             ORDER BY {$sort} {$order}
             LIMIT ?i OFFSET ?i",
            ...$list_params
        );

        return [
            'tags'     => $rows ?: [],
            'total'    => $total,
            'pages'    => $total > 0 ? (int) ceil( $total / $per_page ) : 1,
            'page'     => $page,
            'per_page' => $per_page,
        ];
    }

    // ══════════════════════════════════════════════════════════════════════════
    // GET
    // ══════════════════════════════════════════════════════════════════════════

    public function get_tag( int $id ): ?array {
        return $this->db->getRow(
            "SELECT * FROM {$this->db->table('tags')} WHERE id = ?i LIMIT 1",
            $id
        );
    }

    public function find_by_name( string $name ): ?array {
        return $this->db->getRow(
            "SELECT * FROM {$this->db->table('tags')} WHERE name = ?s LIMIT 1",
            trim( $name )
        );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // CREATE
    // ══════════════════════════════════════════════════════════════════════════

    public function create( string $name ): int|false {
        $name = trim( $name );
        if ( $name === '' ) {
            return false;
        }

        // Check for duplicate.
        $existing = $this->find_by_name( $name );
        if ( $existing ) {
            return (int) $existing['id'];
        }

        $slug = $this->generate_slug( $name );

        return $this->db->insert( 'tags', [
            'name' => sanitize_text_field( $name ),
            'slug' => $slug,
        ] );
    }

    /**
     * Find or create a tag by name. Returns the tag ID.
     */
    public function find_or_create( string $name ): int|false {
        $existing = $this->find_by_name( trim( $name ) );
        if ( $existing ) {
            return (int) $existing['id'];
        }
        return $this->create( $name );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // UPDATE
    // ══════════════════════════════════════════════════════════════════════════

    public function update( int $id, array $data ): bool {
        $fields = [];
        if ( isset( $data['name'] ) ) {
            $name = trim( $data['name'] );
            if ( $name === '' ) return false;
            $fields['name'] = sanitize_text_field( $name );
            $fields['slug'] = $this->generate_slug( $name, $id );
        }
        if ( empty( $fields ) ) {
            return false;
        }
        $this->db->update( 'tags', $fields, [ 'id' => $id ] );
        return true;
    }

    // ══════════════════════════════════════════════════════════════════════════
    // DELETE
    // ══════════════════════════════════════════════════════════════════════════

    public function delete( int $id ): bool {
        $this->db->delete( 'product_tags', [ 'tag_id' => $id ] );
        $this->db->delete( 'tags', [ 'id' => $id ] );
        return true;
    }

    // ══════════════════════════════════════════════════════════════════════════
    // MERGE — combine source tags into target
    // ══════════════════════════════════════════════════════════════════════════

    public function merge( array $source_ids, int $target_id ): bool {
        $target = $this->get_tag( $target_id );
        if ( ! $target ) return false;

        $pt = $this->db->table( 'product_tags' );

        foreach ( $source_ids as $sid ) {
            $sid = (int) $sid;
            if ( $sid === $target_id ) continue;

            // Move product associations: skip duplicates.
            $products = $this->db->getArray(
                "SELECT product_id FROM {$pt} WHERE tag_id = ?i",
                $sid
            );
            foreach ( $products as $row ) {
                $exists = $this->db->getField(
                    "SELECT product_id FROM {$pt} WHERE product_id = ?i AND tag_id = ?i",
                    (int) $row['product_id'], $target_id
                );
                if ( ! $exists ) {
                    $this->db->insert( 'product_tags', [
                        'product_id' => (int) $row['product_id'],
                        'tag_id'     => $target_id,
                    ] );
                }
            }

            // Delete source.
            $this->delete( $sid );
        }

        $this->recount( $target_id );
        return true;
    }

    // ══════════════════════════════════════════════════════════════════════════
    // PRODUCT ASSOCIATIONS
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Sync tags for a product — accepts array of tag names.
     * Creates tags that don't exist yet.
     */
    public function sync_product_tags( int $product_id, array $tag_names ): void {
        $pt = $this->db->table( 'product_tags' );

        // Get current tag IDs.
        $current = $this->db->getArray(
            "SELECT tag_id FROM {$pt} WHERE product_id = ?i",
            $product_id
        );
        $current_ids = array_map( function( $r ) { return (int) $r['tag_id']; }, $current );

        // Resolve names to IDs.
        $new_ids = [];
        foreach ( $tag_names as $name ) {
            $name = trim( $name );
            if ( $name === '' ) continue;
            $tid = $this->find_or_create( $name );
            if ( $tid ) $new_ids[] = $tid;
        }
        $new_ids = array_unique( $new_ids );

        // Add missing.
        $to_add = array_diff( $new_ids, $current_ids );
        foreach ( $to_add as $tid ) {
            $this->db->insert( 'product_tags', [
                'product_id' => $product_id,
                'tag_id'     => $tid,
            ] );
        }

        // Remove old.
        $to_remove = array_diff( $current_ids, $new_ids );
        foreach ( $to_remove as $tid ) {
            $this->db->delete( 'product_tags', [
                'product_id' => $product_id,
                'tag_id'     => $tid,
            ] );
        }

        // Recount affected tags.
        foreach ( array_unique( array_merge( $current_ids, $new_ids ) ) as $tid ) {
            $this->recount( $tid );
        }
    }

    /**
     * Get tag names for a product.
     */
    public function get_product_tags( int $product_id ): array {
        return $this->db->getArray(
            "SELECT t.* FROM {$this->db->table('tags')} t
             INNER JOIN {$this->db->table('product_tags')} pt ON pt.tag_id = t.id
             WHERE pt.product_id = ?i
             ORDER BY t.name ASC",
            $product_id
        );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // HELPERS
    // ══════════════════════════════════════════════════════════════════════════

    public function recount( int $tag_id ): void {
        $count = (int) $this->db->getField(
            "SELECT COUNT(*) FROM {$this->db->table('product_tags')} WHERE tag_id = ?i",
            $tag_id
        );
        $this->db->update( 'tags', [ 'product_count' => $count ], [ 'id' => $tag_id ] );
    }

    public function recount_all(): void {
        $tags = $this->db->getArray( "SELECT id FROM {$this->db->table('tags')}" );
        foreach ( $tags as $t ) {
            $this->recount( (int) $t['id'] );
        }
    }

    private function generate_slug( string $name, ?int $exclude_id = null ): string {
        $base = sanitize_title( $name );
        $slug = $base;
        $i    = 2;

        while ( true ) {
            $where = $exclude_id
                ? "slug = ?s AND id != ?i"
                : "slug = ?s";
            $params = $exclude_id ? [ $slug, $exclude_id ] : [ $slug ];
            $exists = $this->db->getField(
                "SELECT id FROM {$this->db->table('tags')} WHERE {$where} LIMIT 1",
                ...$params
            );
            if ( ! $exists ) break;
            $slug = $base . '-' . $i++;
        }

        return $slug;
    }
}
