<?php

if ( ! defined( 'ABSPATH' ) ) { exit; }

if (!defined('ABSPATH')) { exit; }

class AIEO_DMM_Term_Relations {

    const CACHE_GROUP = 'aieo_dmm';
    const CACHE_TTL   = HOUR_IN_SECONDS;

    
    private static $request_cache = array();

    public static function table() {
        global $wpdb;
        return $wpdb->prefix . 'aieo_dmm_term_relations';
    }

    
    public static function for_term($taxonomy, $term_id, $kind = null, $subtype = null) {
        $taxonomy = (string) $taxonomy;
        $term_id  = (int) $term_id;
        if ($taxonomy === '' || $term_id <= 0) { return array(); }

        $cache_key = self::cache_key($taxonomy, $term_id);
        if (isset(self::$request_cache[$cache_key])) {
            $rows = self::$request_cache[$cache_key];
        } else {
            $cached = wp_cache_get($cache_key, self::CACHE_GROUP);
            if (is_array($cached)) {
                $rows = $cached;
            } else {
                global $wpdb;
                $table = self::table();
                $rows = $wpdb->get_results($wpdb->prepare(
                    "SELECT target_kind, target_subtype, target_id, weight
                       FROM {$table}
                      WHERE source_taxonomy = %s
                        AND source_term_id = %d
                      ORDER BY weight DESC, id ASC",
                    $taxonomy, $term_id
                ), ARRAY_A);
                $rows = is_array($rows) ? $rows : array();
                wp_cache_set($cache_key, $rows, self::CACHE_GROUP, self::CACHE_TTL);
            }
            self::$request_cache[$cache_key] = $rows;
        }

        $out = array();
        foreach ($rows as $r) {
            if ($kind !== null && (string) $r['target_kind'] !== (string) $kind) { continue; }
            if ($subtype !== null && (string) $r['target_subtype'] !== (string) $subtype) { continue; }
            $out[] = array(
                'kind'    => (string) $r['target_kind'],
                'subtype' => (string) $r['target_subtype'],
                'id'      => (int) $r['target_id'],
            );
        }
        return $out;
    }

    
    public static function ids_for_term($taxonomy, $term_id, $kind, $subtype) {
        $rows = self::for_term($taxonomy, $term_id, $kind, $subtype);
        return array_map(static function ($r) { return (int) $r['id']; }, $rows);
    }

    
    public static function for_post($post_id, $post_type = null) {
        $post_id = (int) $post_id;
        if ($post_id <= 0) { return array(); }

        global $wpdb;
        $table = self::table();
        if ($post_type === null) {
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT source_taxonomy, source_term_id
                   FROM {$table}
                  WHERE target_kind = 'post'
                    AND target_id = %d
                  ORDER BY weight DESC, id ASC",
                $post_id
            ), ARRAY_A);
        } else {
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT source_taxonomy, source_term_id
                   FROM {$table}
                  WHERE target_kind = 'post'
                    AND target_subtype = %s
                    AND target_id = %d
                  ORDER BY weight DESC, id ASC",
                (string) $post_type, $post_id
            ), ARRAY_A);
        }
        $rows = is_array($rows) ? $rows : array();
        $out = array();
        foreach ($rows as $r) {
            $out[] = array(
                'taxonomy' => (string) $r['source_taxonomy'],
                'term_id'  => (int) $r['source_term_id'],
            );
        }
        return $out;
    }

    
    public static function set_targets($source_taxonomy, $source_term_id, $kind, $subtype, array $target_ids) {
        global $wpdb;
        $source_taxonomy = (string) $source_taxonomy;
        $source_term_id  = (int) $source_term_id;
        $kind            = (string) $kind;
        $subtype         = (string) $subtype;
        if ($source_taxonomy === '' || $source_term_id <= 0 || $kind === '' || $subtype === '') {
            return 0;
        }
        if (!in_array($kind, array('term', 'post'), true)) { return 0; }

        $table = self::table();

        
        $previous = $wpdb->get_col($wpdb->prepare(
            "SELECT target_id FROM {$table}
              WHERE source_taxonomy = %s
                AND source_term_id  = %d
                AND target_kind     = %s
                AND target_subtype  = %s",
            $source_taxonomy, $source_term_id, $kind, $subtype
        ));
        $previous = is_array($previous) ? array_map('intval', $previous) : array();

        
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$table}
              WHERE source_taxonomy = %s
                AND source_term_id  = %d
                AND target_kind     = %s
                AND target_subtype  = %s",
            $source_taxonomy, $source_term_id, $kind, $subtype
        ));
        if ($kind === 'term' && !empty($previous)) {
            $placeholders = implode(',', array_fill(0, count($previous), '%d'));
            $wpdb->query($wpdb->prepare(
                "DELETE FROM {$table}
                  WHERE source_taxonomy = %s
                    AND target_kind = 'term'
                    AND target_subtype = %s
                    AND target_id = %d
                    AND source_term_id IN ({$placeholders})",
                array_merge(array($subtype, $source_taxonomy, $source_term_id), $previous)
            ));
        }

        
        $clean_ids = array();
        foreach ($target_ids as $tid) {
            $tid = (int) $tid;
            if ($tid <= 0) { continue; }
            
            if ($kind === 'term' && $subtype === $source_taxonomy && $tid === $source_term_id) { continue; }
            $clean_ids[$tid] = true;
        }
        $clean_ids = array_keys($clean_ids);

        $written = 0;
        $now = current_time('mysql', true);
        foreach ($clean_ids as $tid) {
            
            $wpdb->query($wpdb->prepare(
                "INSERT IGNORE INTO {$table}
                  (source_taxonomy, source_term_id, target_kind, target_subtype, target_id, rel_type, weight, created_at)
                 VALUES (%s, %d, %s, %s, %d, 'related', 0, %s)",
                $source_taxonomy, $source_term_id, $kind, $subtype, $tid, $now
            ));
            $written++;
            
            if ($kind === 'term') {
                $wpdb->query($wpdb->prepare(
                    "INSERT IGNORE INTO {$table}
                      (source_taxonomy, source_term_id, target_kind, target_subtype, target_id, rel_type, weight, created_at)
                     VALUES (%s, %d, 'term', %s, %d, 'related', 0, %s)",
                    $subtype, $tid, $source_taxonomy, $source_term_id, $now
                ));
                $written++;
            }
        }

        
        self::bust_source($source_taxonomy, $source_term_id);
        if ($kind === 'term') {
            foreach (array_unique(array_merge($previous, $clean_ids)) as $tid) {
                self::bust_source($subtype, (int) $tid);
            }
        }

        return $written;
    }

    
    public static function add_edge($source_taxonomy, $source_term_id, $kind, $subtype, $target_id) {
        global $wpdb;
        $source_taxonomy = (string) $source_taxonomy;
        $source_term_id  = (int) $source_term_id;
        $kind            = (string) $kind;
        $subtype         = (string) $subtype;
        $target_id       = (int) $target_id;
        if ($source_taxonomy === '' || $source_term_id <= 0 || $target_id <= 0) { return false; }
        if (!in_array($kind, array('term', 'post'), true))                       { return false; }
        if ($kind === 'term' && $subtype === $source_taxonomy && $target_id === $source_term_id) {
            return false; 
        }

        $table = self::table();
        $now = current_time('mysql', true);

        $wpdb->query($wpdb->prepare(
            "INSERT IGNORE INTO {$table}
              (source_taxonomy, source_term_id, target_kind, target_subtype, target_id, rel_type, weight, created_at)
             VALUES (%s, %d, %s, %s, %d, 'related', 0, %s)",
            $source_taxonomy, $source_term_id, $kind, $subtype, $target_id, $now
        ));
        if ($kind === 'term') {
            $wpdb->query($wpdb->prepare(
                "INSERT IGNORE INTO {$table}
                  (source_taxonomy, source_term_id, target_kind, target_subtype, target_id, rel_type, weight, created_at)
                 VALUES (%s, %d, 'term', %s, %d, 'related', 0, %s)",
                $subtype, $target_id, $source_taxonomy, $source_term_id, $now
            ));
            self::bust_source($subtype, $target_id);
        }
        self::bust_source($source_taxonomy, $source_term_id);
        return true;
    }

    
    public static function delete_edge($source_taxonomy, $source_term_id, $kind, $subtype, $target_id) {
        global $wpdb;
        $source_taxonomy = (string) $source_taxonomy;
        $source_term_id  = (int) $source_term_id;
        $kind            = (string) $kind;
        $subtype         = (string) $subtype;
        $target_id       = (int) $target_id;
        if ($source_taxonomy === '' || $source_term_id <= 0 || $target_id <= 0) { return false; }

        $table = self::table();

        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$table}
              WHERE source_taxonomy = %s
                AND source_term_id  = %d
                AND target_kind     = %s
                AND target_subtype  = %s
                AND target_id       = %d",
            $source_taxonomy, $source_term_id, $kind, $subtype, $target_id
        ));
        if ($kind === 'term') {
            $wpdb->query($wpdb->prepare(
                "DELETE FROM {$table}
                  WHERE source_taxonomy = %s
                    AND source_term_id  = %d
                    AND target_kind     = 'term'
                    AND target_subtype  = %s
                    AND target_id       = %d",
                $subtype, $target_id, $source_taxonomy, $source_term_id
            ));
            self::bust_source($subtype, $target_id);
        }
        self::bust_source($source_taxonomy, $source_term_id);
        return true;
    }

    
    public static function delete_for_source($source_taxonomy, $source_term_id) {
        global $wpdb;
        $source_taxonomy = (string) $source_taxonomy;
        $source_term_id  = (int) $source_term_id;
        if ($source_taxonomy === '' || $source_term_id <= 0) { return 0; }

        $table = self::table();

        
        $neighbours = $wpdb->get_results($wpdb->prepare(
            "SELECT target_subtype AS tax, target_id AS term_id
               FROM {$table}
              WHERE source_taxonomy = %s
                AND source_term_id  = %d
                AND target_kind     = 'term'",
            $source_taxonomy, $source_term_id
        ), ARRAY_A);

        
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$table}
              WHERE source_taxonomy = %s AND source_term_id = %d",
            $source_taxonomy, $source_term_id
        ));
        
        $wpdb->query($wpdb->prepare(
            "DELETE FROM {$table}
              WHERE target_kind = 'term'
                AND target_subtype = %s
                AND target_id = %d",
            $source_taxonomy, $source_term_id
        ));

        self::bust_source($source_taxonomy, $source_term_id);
        if (is_array($neighbours)) {
            foreach ($neighbours as $n) {
                self::bust_source((string) $n['tax'], (int) $n['term_id']);
            }
        }
        return 1;
    }

    private static function cache_key($taxonomy, $term_id) {
        return 'aieo_tr:' . (string) $taxonomy . ':' . (int) $term_id;
    }

    private static function bust_source($taxonomy, $term_id) {
        $key = self::cache_key($taxonomy, $term_id);
        unset(self::$request_cache[$key]);
        wp_cache_delete($key, self::CACHE_GROUP);
    }

    
    public static function flush_request_cache() {
        self::$request_cache = array();
    }

    
    public static function bootstrap() {
        static $done = false;
        if ($done) { return; }
        $done = true;
        add_action('delete_term', array(__CLASS__, 'on_delete_term'), 10, 3);
    }

    public static function on_delete_term($term_id, $tt_id, $taxonomy) {
        self::delete_for_source((string) $taxonomy, (int) $term_id);
    }
}
