<?php
/**
 * Angebots-Modell: bereinigt die Eingabedaten, berechnet Summen und
 * stellt Hilfsfunktionen für Dateiname und Nummer bereit.
 */

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

class AG_Offer {

    /** Sprachen für das Dropdown (Kürzel => Anzeige) */
    const LANGUAGES = array(
        'DE' => 'alemán',
        'EN' => 'inglés',
        'ES' => 'español',
        'CAT' => 'catalán',
        'FR' => 'francés',
    );

    /** Einheiten */
    const UNITS = array(
        'hl' => 'hl (45 min)',
        'h'  => 'h (60 min)',
    );

    /**
     * Preiskatalog (Grundpreise pro Einheit) aus der internen Preistabelle.
     * Je Kursart die Basis-Preise pro UE (45 min = "hl") und pro h (60 min).
     * Diese Werte dienen als Vorbelegung im Formular und bleiben überschreibbar.
     *
     * key => array( label, hl (Preis pro UE/45min), h (Preis pro h/60min) )
     */
    public static function price_catalog() {
        // Rabatt-Staffeln: array( array( min, max|null, prozent ), ... )
        // hl = UE (45 min): 10–19 / 20–39 / 40–59 / 60–79 / 80+
        // h  = Stunden (60 min): 10–14 / 15–29 / 30–44 / 45–59 / 60+
        $tiers = function ( $p1, $p2, $p3, $p4, $p5 ) {
            return array(
                'disc_hl' => array(
                    array( 10, 19, $p1 ), array( 20, 39, $p2 ), array( 40, 59, $p3 ),
                    array( 60, 79, $p4 ), array( 80, null, $p5 ),
                ),
                'disc_h' => array(
                    array( 10, 14, $p1 ), array( 15, 29, $p2 ), array( 30, 44, $p3 ),
                    array( 45, 59, $p4 ), array( 60, null, $p5 ),
                ),
            );
        };
        $online   = $tiers( 4, 8, 12, 16, 20 );  // Erwachsene Online (1/2/3 TN)
        $praes1   = $tiers( 2, 4, 8, 12, 14 );   // Erwachsene Präsenz/Gemischt (1 TN)
        $praes2   = $tiers( 2, 8, 12, 22, 32 );  // Erwachsene Präsenz (2 TN)
        $kj       = $tiers( 2, 4, 6, 8, 10 );    // Kinder & Jugendliche / Erwachsene Spanisch

        return array(
            'erw_online_1tn'    => array( 'label' => 'Erwachsene · 100% Online (1 TN)',        'hl' => 45.00, 'h' => 60.00 ) + $online,
            'erw_praesenz_1tn'  => array( 'label' => 'Erwachsene · Präsenz/Gemischt (1 TN)',    'hl' => 45.00, 'h' => 60.00 ) + $praes1,
            'kj_de_1tn'         => array( 'label' => 'Kinder & Jugendliche Deutsch bis 16 J. (1 TN)', 'hl' => 37.43, 'h' => 49.90 ) + $kj,
            'erw_es_1tn'        => array( 'label' => 'Erwachsene Spanisch (1 TN)',              'hl' => 37.43, 'h' => 49.90 ) + $kj,
            'erw_online_2tn'    => array( 'label' => 'Erwachsene · Online (2 TN)',              'hl' => 46.50, 'h' => 63.90 ) + $online,
            'erw_praesenz_2tn'  => array( 'label' => 'Erwachsene · Präsenz (2 TN)',             'hl' => 46.50, 'h' => 62.00 ) + $praes2,
            'kj_de_2tn'         => array( 'label' => 'Kinder & Jugendliche Deutsch bis 16 J. (2 TN)', 'hl' => 38.93, 'h' => 51.90 ) + $kj,
            'erw_es_2tn'        => array( 'label' => 'Erwachsene Spanisch (2 TN)',              'hl' => 38.93, 'h' => 51.90 ) + $kj,
            'erw_online_3tn'    => array( 'label' => 'Erwachsene · Online (3 TN)',              'hl' => 48.00, 'h' => 64.00 ) + $online,
            'kj_de_3tn'         => array( 'label' => 'Kinder & Jugendliche Deutsch bis 16 J. (3 TN)', 'hl' => 40.43, 'h' => 53.90 ) + $kj,
            'erw_es_3tn'        => array( 'label' => 'Erwachsene Spanisch (3 TN)',              'hl' => 40.43, 'h' => 53.90 ) + $kj,
        );
    }

    /**
     * Ermittelt den Mengenrabatt-Prozentsatz für eine Kursart/Einheit/Menge.
     *
     * @param string $course_key Katalog-Schlüssel (z. B. 'erw_online_1tn')
     * @param string $unit       'hl' oder 'h'
     * @param float  $units      Anzahl Einheiten
     * @return float Prozentsatz (z. B. 12.0) oder 0
     */
    public static function discount_pct( $course_key, $unit, $units ) {
        $cat = self::price_catalog();
        if ( ! isset( $cat[ $course_key ] ) ) {
            return 0.0;
        }
        $key   = ( 'h' === $unit ) ? 'disc_h' : 'disc_hl';
        $tiers = isset( $cat[ $course_key ][ $key ] ) ? $cat[ $course_key ][ $key ] : array();
        foreach ( $tiers as $t ) {
            if ( $units >= $t[0] && ( null === $t[1] || $units <= $t[1] ) ) {
                return (float) $t[2];
            }
        }
        return 0.0;
    }

    /**
     * Bereinigt und normalisiert die Rohdaten aus dem Formular.
     *
     * @param array $raw
     * @return array
     */
    public static function sanitize( array $raw ) {
        $offer = array();

        // Kopf / Nummer / Datum
        $offer['number']      = sanitize_text_field( $raw['number'] ?? '' );
        $offer['offer_date']  = self::sanitize_date( $raw['offer_date'] ?? '' );

        // Kunde
        $offer['client_name'] = sanitize_text_field( $raw['client_name'] ?? '' );
        $offer['first_name']  = sanitize_text_field( $raw['first_name'] ?? '' );
        $offer['last_name']   = sanitize_text_field( $raw['last_name'] ?? '' );
        $offer['dni']         = sanitize_text_field( $raw['dni'] ?? '' );
        $offer['address']     = sanitize_text_field( $raw['address'] ?? '' );

        // Metadaten für Dateiname
        $offer['language']    = sanitize_text_field( $raw['language'] ?? 'DE' );
        $offer['volume']      = sanitize_text_field( $raw['volume'] ?? '' );
        $offer['unit']        = in_array( ( $raw['unit'] ?? 'hl' ), array( 'h', 'hl' ), true ) ? $raw['unit'] : 'hl';

        // Dokumentsprache des Angebots (ES/EN/DE)
        $doc_lang = strtoupper( sanitize_text_field( $raw['doc_language'] ?? 'ES' ) );
        if ( ! array_key_exists( $doc_lang, AG_I18n::DOC_LANGUAGES ) ) {
            $doc_lang = 'ES';
        }
        $offer['doc_language'] = $doc_lang;

        // Einleitungstext (anpassbar; leer = Standardtext der Dokumentsprache)
        $offer['intro']       = wp_kses_post( trim( $raw['intro'] ?? '' ) );

        // Einheit als Anzeigetext (h / hl) für die Concepto-Sätze
        $unit_label = ( 'h' === $offer['unit'] ) ? 'h' : 'hl';

        // Positionen mit strukturierten Concepto-Bausteinen
        $offer['items'] = array();
        if ( ! empty( $raw['items'] ) && is_array( $raw['items'] ) ) {
            foreach ( $raw['items'] as $item ) {
                $qty   = self::to_float( $item['qty'] ?? '' );
                $price = self::to_float( $item['price'] ?? '' );
                $units = self::to_float( $item['units'] ?? '' );

                // Wochentage (JS-Nummern 0=So … 6=Sa) aus den Checkboxen
                $weekdays = array();
                if ( ! empty( $item['c_weekdays'] ) && is_array( $item['c_weekdays'] ) ) {
                    foreach ( $item['c_weekdays'] as $w ) {
                        $w = (int) $w;
                        if ( $w >= 0 && $w <= 6 ) {
                            $weekdays[] = $w;
                        }
                    }
                    $weekdays = array_values( array_unique( $weekdays ) );
                }

                // Concepto-Bausteine
                $parts = array(
                    'taught'   => sanitize_text_field( $item['c_taught'] ?? $offer['language'] ),
                    'motivo'   => sanitize_text_field( $item['c_motivo'] ?? '' ),
                    'sessions' => sanitize_text_field( $item['c_sessions'] ?? '' ),
                    'duration' => sanitize_text_field( $item['c_duration'] ?? '' ),
                    'unit'     => $unit_label,
                    'from'     => self::sanitize_date_display( $item['c_from'] ?? '' ),
                    'to'       => self::sanitize_date_display( $item['c_to'] ?? '' ),
                    'modality' => sanitize_text_field( $item['c_modality'] ?? '' ),
                    'weekdays' => $weekdays,
                );

                $manual = sanitize_textarea_field( $item['concept'] ?? '' );

                // Manueller Text hat Vorrang, sonst Satz aus Bausteinen bauen.
                $has_parts = ( '' !== $parts['motivo'] || '' !== $parts['sessions'] || '' !== $parts['duration'] || '' !== $parts['from'] || '' !== $parts['to'] || '' !== $parts['modality'] || ! empty( $weekdays ) );
                if ( '' !== $manual ) {
                    $desc = $manual;
                } elseif ( $has_parts ) {
                    $desc = AG_I18n::compose_concept( $parts, $doc_lang );
                } else {
                    $desc = '';
                }

                // Leere Zeilen überspringen: Eine Position zählt nur, wenn sie
                // einen Concepto-Text ODER Preis ODER Einheiten hat. Die mit 1
                // vorbelegte Cantidad allein macht eine Zeile nicht "gefüllt".
                if ( '' === trim( $desc ) && 0.0 === $price && 0.0 === $units ) {
                    continue;
                }

                // Kursart (Katalog-Schlüssel) + Mengenrabatt-Prozentsatz
                $course_key = sanitize_key( $item['course'] ?? '' );
                $disc_pct   = $course_key ? self::discount_pct( $course_key, $offer['unit'], $units ) : 0.0;

                $offer['items'][] = array(
                    'qty'          => ( $qty > 0 ) ? $qty : 1,
                    'concept'      => $desc,
                    'concept_parts' => $parts,
                    'concept_manual' => $manual,
                    'course'       => $course_key,
                    'price'        => $price,
                    'units'        => $units,
                    'amount'       => round( $price * $units, 2 ),
                    'disc_pct'     => $disc_pct,
                );
            }
        }

        // Rabatt (Descuento): Ist das Feld leer, wird der Mengenrabatt
        // automatisch aus den Staffeln der gewählten Kursarten berechnet.
        // Ein eingetragener Wert (auch 0) hat immer Vorrang.
        $auto_discount = 0.0;
        foreach ( $offer['items'] as $it ) {
            if ( ! empty( $it['disc_pct'] ) ) {
                $auto_discount += round( $it['amount'] * $it['disc_pct'] / 100, 2 );
            }
        }
        $offer['discount_auto'] = round( $auto_discount, 2 );

        $discount_raw       = trim( (string) ( $raw['discount'] ?? '' ) );
        $offer['discount']  = ( '' === $discount_raw ) ? $offer['discount_auto'] : self::to_float( $discount_raw );

        // Zahlungsart: Beide Optionen werden im PDF zur Auswahl durch die
        // Kundschaft angeboten (leere Ankreuzkästchen). Keine Vorauswahl.
        $offer['payment']   = 'both';

        // Volumen für den Dateinamen: wenn leer, automatisch aus den Positionen
        // ableiten (Einheiten der ersten Position mit Einheiten > 0 + Einheit).
        if ( '' === trim( $offer['volume'] ) ) {
            foreach ( $offer['items'] as $it ) {
                if ( (float) $it['units'] > 0 ) {
                    $offer['volume'] = self::num_plain( $it['units'] ) . $unit_label;
                    break;
                }
            }
        }

        // Fußnote / Bemerkungen (anpassbar)
        $offer['notes']     = wp_kses_post( trim( $raw['notes'] ?? '' ) );

        // Summen berechnen
        $offer = array_merge( $offer, self::calculate( $offer ) );

        return $offer;
    }

    /**
     * Berechnet Zwischensumme, Rabatt und Gesamtsumme.
     *
     * @param array $offer
     * @return array{sum_items:float, discount:float, total:float}
     */
    public static function calculate( array $offer ) {
        $sum = 0.0;
        foreach ( $offer['items'] as $item ) {
            $sum += (float) $item['amount'];
        }

        $discount = isset( $offer['discount'] ) ? (float) $offer['discount'] : 0.0;
        $total    = max( 0, $sum - $discount );

        return array(
            'sum_items' => round( $sum, 2 ),
            'discount'  => round( $discount, 2 ),
            'total'     => round( $total, 2 ),
        );
    }

    /**
     * Baut den Speichernamen:
     * "YY_0000_Nachname_Vorname_Sprache_Volumen_ddmmyy"
     *
     * @param array $offer
     * @return string (ohne Dateiendung)
     */
    public static function build_filename( array $offer ) {
        $number = str_replace( array( '/', ' ' ), '_', $offer['number'] );

        $parts = array(
            $number,
            self::slug_part( $offer['last_name'] ),
            self::slug_part( $offer['first_name'] ),
            self::slug_part( $offer['language'] ),
            self::slug_part( $offer['volume'] ),
        );

        // Datum ddmmyy aus offer_date (Y-m-d)
        $date_part = '';
        if ( ! empty( $offer['offer_date'] ) ) {
            $ts = strtotime( $offer['offer_date'] );
            if ( $ts ) {
                $date_part = gmdate( 'dmy', $ts );
            }
        }
        $parts[] = $date_part;

        // leere Teile entfernen, mit _ verbinden
        $parts = array_filter( $parts, static function ( $p ) {
            return '' !== $p;
        } );

        return implode( '_', $parts );
    }

    /**
     * Wandelt einen deutschen/spanischen Zahlenstring in float
     * ("1.234,56" oder "42,75" oder "42.75").
     */
    /**
     * Zahl ohne Tausenderpunkte/Dezimalnullen für den Dateinamen (40 statt 40,00).
     */
    private static function num_plain( $value ) {
        $value = (float) $value;
        if ( floor( $value ) === $value ) {
            return (string) (int) $value;
        }
        return rtrim( rtrim( number_format( $value, 2, '.', '' ), '0' ), '.' );
    }

    public static function to_float( $value ) {
        $value = trim( (string) $value );
        if ( '' === $value ) {
            return 0.0;
        }
        // Tausenderpunkte entfernen wenn Komma als Dezimaltrenner vorhanden
        if ( false !== strpos( $value, ',' ) ) {
            $value = str_replace( array( '.', ' ' ), '', $value );
            $value = str_replace( ',', '.', $value );
        }
        $value = preg_replace( '/[^0-9.\-]/', '', $value );
        return (float) $value;
    }

    /**
     * Formatiert einen Betrag als "1.234,56 EUR" (spanisch/deutsch).
     */
    public static function format_money( $value, $with_currency = true ) {
        $formatted = number_format( (float) $value, 2, ',', '.' );
        return $with_currency ? $formatted . ' EUR' : $formatted;
    }

    /**
     * Formatiert ein Datum (Y-m-d) als "DD.MM.AAAA".
     */
    public static function format_date_display( $ymd ) {
        if ( empty( $ymd ) ) {
            return '';
        }
        $ts = strtotime( $ymd );
        return $ts ? gmdate( 'd.m.Y', $ts ) : $ymd;
    }

    private static function sanitize_date( $value ) {
        $value = trim( (string) $value );
        if ( '' === $value ) {
            return '';
        }
        // Erwartet Y-m-d (vom <input type=date>); sonst versuchen zu parsen.
        $ts = strtotime( $value );
        return $ts ? gmdate( 'Y-m-d', $ts ) : '';
    }

    /**
     * Wandelt ein Datum (aus <input type=date>, Y-m-d) in "DD.MM.YYYY" um.
     * Leerer Wert bleibt leer.
     */
    private static function sanitize_date_display( $value ) {
        $value = trim( (string) $value );
        if ( '' === $value ) {
            return '';
        }
        $ts = strtotime( $value );
        return $ts ? gmdate( 'd.m.Y', $ts ) : sanitize_text_field( $value );
    }

    /**
     * Macht einen Namensteil dateinamensicher (ohne Umlaut-/Sonderzeichen-Chaos).
     */
    private static function slug_part( $text ) {
        $text = trim( (string) $text );
        if ( '' === $text ) {
            return '';
        }
        $map = array(
            'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'Ä' => 'Ae', 'Ö' => 'Oe', 'Ü' => 'Ue', 'ß' => 'ss',
            'á' => 'a', 'é' => 'e', 'í' => 'i', 'ó' => 'o', 'ú' => 'u', 'ñ' => 'n',
            'Á' => 'A', 'É' => 'E', 'Í' => 'I', 'Ó' => 'O', 'Ú' => 'U', 'Ñ' => 'N',
        );
        $text = strtr( $text, $map );
        $text = preg_replace( '/[^A-Za-z0-9]+/', '', $text );
        return $text;
    }
}
