"""Chord quality -> interval structure mapping.

Each chord quality maps to a tuple of ``(SIC, tone_functions)`` pairs where
``tone_functions`` is a tuple of one or more :class:`ToneFunction` members.
Standard chords have a single ToneFunction per component; augmented sixth
chords have multiple functions per component, derived from their resolution
labels.

SIC is imported from the pitchspace paradigm module.
"""

from __future__ import annotations

from flexohr.harmony.harmony_enums import (
    ChordClass,
    ChordQuality,
    CollectionType,
    Inversion,
    ToneFunction,
)
from flexohr.paradigms.pitchspace import SIC

# Using the function name `tf` as shorthand
tf = ToneFunction

# Type: dict[ChordQuality, tuple[tuple[SIC, tuple[ToneFunction, ...]], ...]]
# Each entry is (interval, (tone_function_1, tone_function_2, ...))
CHORD_QUALITY_INTERVALS: dict[ChordQuality, tuple[tuple[SIC, tuple[ToneFunction, ...]], ...]] = {
    ChordQuality.major_triad: (
        (SIC("P1"), (tf.root,)),
        (SIC("M3"), (tf.third,)),
        (SIC("P5"), (tf.fifth,)),
    ),
    ChordQuality.minor_triad: (
        (SIC("P1"), (tf.root,)),
        (SIC("m3"), (tf.third,)),
        (SIC("P5"), (tf.fifth,)),
    ),
    ChordQuality.diminished_triad: (
        (SIC("P1"), (tf.root,)),
        (SIC("m3"), (tf.third,)),
        (SIC("d5"), (tf.fifth,)),
    ),
    ChordQuality.augmented_triad: (
        (SIC("P1"), (tf.root,)),
        (SIC("M3"), (tf.third,)),
        (SIC("a5"), (tf.fifth,)),
    ),
    ChordQuality.dominant_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("M3"), (tf.third,)),
        (SIC("P5"), (tf.fifth,)),
        (SIC("m7"), (tf.seventh,)),
    ),
    ChordQuality.minor_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("m3"), (tf.third,)),
        (SIC("P5"), (tf.fifth,)),
        (SIC("m7"), (tf.seventh,)),
    ),
    ChordQuality.major_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("M3"), (tf.third,)),
        (SIC("P5"), (tf.fifth,)),
        (SIC("M7"), (tf.seventh,)),
    ),
    ChordQuality.minor_major_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("m3"), (tf.third,)),
        (SIC("P5"), (tf.fifth,)),
        (SIC("M7"), (tf.seventh,)),
    ),
    ChordQuality.diminished_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("m3"), (tf.third,)),
        (SIC("d5"), (tf.fifth,)),
        (SIC("d7"), (tf.seventh,)),
    ),
    ChordQuality.half_diminished_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("m3"), (tf.third,)),
        (SIC("d5"), (tf.fifth,)),
        (SIC("m7"), (tf.seventh,)),
    ),
    ChordQuality.augmented_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("M3"), (tf.third,)),
        (SIC("a5"), (tf.fifth,)),
        (SIC("m7"), (tf.seventh,)),
    ),
    ChordQuality.augmented_major_seventh: (
        (SIC("P1"), (tf.root,)),
        (SIC("M3"), (tf.third,)),
        (SIC("a5"), (tf.fifth,)),
        (SIC("M7"), (tf.seventh,)),
    ),
    # Augmented sixths — intervals measured from the bass note (NOT the root).
    # Tone functions are derived from the resolution labels:
    #
    #   It6  resolves as viio(b3)/V:
    #     P1  = bass (= third of resolution, altered, downward leading tone)
    #     M3  = fifth of resolution
    #     a6  = root of resolution (= leading tone, resolves upward)
    #
    #   Ger6 resolves as viio65(b3)/V:
    #     P1  = bass (= third of resolution, altered, downward leading tone)
    #     M3  = fifth of resolution
    #     P5  = seventh of resolution
    #     a6  = root of resolution (= leading tone, resolves upward)
    #
    #   Fr6  resolves as V43(b5)/V:
    #     P1  = bass (= fifth of resolution, altered, downward leading tone)
    #     M3  = seventh of resolution
    #     a4  = root of resolution
    #     a6  = third of resolution (= leading tone, resolves upward)
    #
    # Default inversion is first ("6") for all three; for Ger/Fr tetrads "6" is
    # understood as this specific inversion even though it would normally not be
    # a valid figbass for tetrads.
    ChordQuality.italian_sixth: (
        (SIC("P1"), (tf.bass, tf.third, tf.altered, tf.downward_leading_tone)),
        (SIC("M3"), (tf.fifth,)),
        (SIC("a6"), (tf.root, tf.leading_tone)),
    ),
    ChordQuality.german_sixth: (
        (SIC("P1"), (tf.bass, tf.third, tf.altered, tf.downward_leading_tone)),
        (SIC("M3"), (tf.fifth,)),
        (SIC("P5"), (tf.seventh,)),
        (SIC("a6"), (tf.root, tf.leading_tone)),
    ),
    ChordQuality.french_sixth: (
        (SIC("P1"), (tf.bass, tf.fifth, tf.altered, tf.downward_leading_tone)),
        (SIC("M3"), (tf.seventh,)),
        (SIC("a4"), (tf.root,)),
        (SIC("a6"), (tf.third, tf.leading_tone)),
    ),
    # Suspended
    ChordQuality.suspended_second: (
        (SIC("P1"), (tf.root,)),
        (SIC("M2"), (tf.suspended_second,)),
        (SIC("P5"), (tf.fifth,)),
    ),
    ChordQuality.suspended_fourth: (
        (SIC("P1"), (tf.root,)),
        (SIC("P4"), (tf.suspended_fourth,)),
        (SIC("P5"), (tf.fifth,)),
    ),
    # Power chord
    ChordQuality.power_chord: (
        (SIC("P1"), (tf.root,)),
        (SIC("P5"), (tf.fifth,)),
    ),
    # Unison
    ChordQuality.unison: ((SIC("P1"), (tf.root,)),),
}


# Derive ChordQuality -> ChordClass mapping from interval count
def _derive_chord_class(quality: ChordQuality) -> ChordClass:
    from flexohr.harmony.harmony_enums import ChordClass

    if quality in (ChordQuality.italian_sixth, ChordQuality.german_sixth, ChordQuality.french_sixth):
        return ChordClass.augmented_sixth
    if quality in (ChordQuality.suspended_second, ChordQuality.suspended_fourth):
        return ChordClass.suspended
    intervals = CHORD_QUALITY_INTERVALS.get(quality)
    if intervals is None:
        return ChordClass.unclassified
    n = len(intervals)
    if n == 1:
        return ChordClass.unclassified
    if n == 2:
        return ChordClass.dyad
    if n == 3:
        return ChordClass.triad
    if n == 4:
        return ChordClass.seventh
    return ChordClass.unclassified


CHORD_QUALITY_TO_CLASS: dict[ChordQuality, ChordClass] = {
    q: _derive_chord_class(q) for q in ChordQuality if q.name == q.value  # skip aliases
}


# ── Inversion tables ──

# Maps inversion index (0-based) to the ToneFunction whose component becomes
# the bass.  Index 0 = root position (bass is root), 1 = first inversion
# (bass is third), etc.
INVERSION_INDEX_TO_TONE_FUNCTION: dict[int, ToneFunction] = {
    0: ToneFunction.root,
    1: ToneFunction.third,
    2: ToneFunction.fifth,
    3: ToneFunction.seventh,
}

# Maps Inversion enum member to 0-based index.
INVERSION_TO_INDEX: dict[Inversion, int] = {
    Inversion.root_position: 0,
    Inversion.first: 1,
    Inversion.second: 2,
    Inversion.third: 3,
}

# Maps 0-based index to Inversion enum member.
INDEX_TO_INVERSION: dict[int, Inversion] = {v: k for k, v in INVERSION_TO_INDEX.items()}

# Maximum inversion for each ChordClass that supports inversion.
# Chord classes not in this dict do NOT support inversion.
CHORD_CLASS_MAX_INVERSION: dict[ChordClass, int] = {
    ChordClass.triad: 2,
    ChordClass.seventh: 3,
    ChordClass.augmented_sixth: 3,
}


# Scale/collection interval tables
# SIC values are positions on the line of fifths (0 = P1, 1 = P5, 2 = M2, etc.)
COLLECTION_TYPE_INTERVALS: dict[CollectionType, tuple[SIC, ...]] = {
    CollectionType.major: (
        SIC(0),
        SIC(2),
        SIC(4),
        SIC(-1),
        SIC(1),
        SIC(3),
        SIC(5),
    ),
    CollectionType.natural_minor: (
        SIC(0),
        SIC(2),
        SIC(-3),
        SIC(-1),
        SIC(1),
        SIC(-4),
        SIC(-2),
    ),
    CollectionType.aeolian: (
        SIC(0),
        SIC(2),
        SIC(-3),
        SIC(-1),
        SIC(1),
        SIC(-4),
        SIC(-2),
    ),
    CollectionType.dorian: (
        SIC(0),
        SIC(2),
        SIC(-3),
        SIC(-1),
        SIC(1),
        SIC(3),
        SIC(-2),
    ),
    CollectionType.phrygian: (
        SIC(0),
        SIC(-5),
        SIC(-3),
        SIC(-1),
        SIC(1),
        SIC(-4),
        SIC(-2),
    ),
    CollectionType.lydian: (
        SIC(0),
        SIC(2),
        SIC(4),
        SIC(6),
        SIC(1),
        SIC(3),
        SIC(5),
    ),
    CollectionType.mixolydian: (
        SIC(0),
        SIC(2),
        SIC(4),
        SIC(-1),
        SIC(1),
        SIC(3),
        SIC(-2),
    ),
    CollectionType.locrian: (
        SIC(0),
        SIC(-5),
        SIC(-3),
        SIC(-1),
        SIC(-6),
        SIC(-4),
        SIC(-2),
    ),
    CollectionType.harmonic_minor: (
        SIC(0),
        SIC(2),
        SIC(-3),
        SIC(-1),
        SIC(1),
        SIC(-4),
        SIC(5),
    ),
    CollectionType.melodic_minor: (
        SIC(0),
        SIC(2),
        SIC(-3),
        SIC(-1),
        SIC(1),
        SIC(3),
        SIC(5),
    ),
    CollectionType.major_pentatonic: (
        SIC(0),
        SIC(2),
        SIC(4),
        SIC(1),
        SIC(3),
    ),
    CollectionType.minor_pentatonic: (
        SIC(0),
        SIC(-3),
        SIC(-1),
        SIC(1),
        SIC(-2),
    ),
}
