#!/usr/bin/env python3
"""NAU7802 Scale Diagnostic.

I2C address: 0x2A
Bus: /dev/i2c-1 (GPIO2/GPIO3 on RPi)
"""

import os
import sys
import time

import smbus2

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "daemon")))

from nau7802 import NAU7802


def _env_int(name: str, default: int) -> int:
    value = os.environ.get(name)
    if value is None or value == "":
        return default
    try:
        return int(value)
    except ValueError:
        return default


I2C_BUS = _env_int("SPOOLBUDDY_I2C_BUS", 1)
NAU7802_ADDR = 0x2A

# Register addresses
REG_PU_CTRL = 0x00
REG_CTRL1 = 0x01
REG_CTRL2 = 0x02
REG_ADCO_B2 = 0x12  # ADC output MSB
REG_ADCO_B1 = 0x13
REG_ADCO_B0 = 0x14  # ADC output LSB
REG_ADC = 0x15
REG_PGA = 0x1B
REG_PWR_CTRL = 0x1C
REG_REVISION = 0x1F

# PU_CTRL bits
PU_RR = 0x01  # Register reset
PU_PUD = 0x02  # Power up digital
PU_PUA = 0x04  # Power up analog
PU_PUR = 0x08  # Power up ready (read-only)
PU_CS = 0x10  # Cycle start
PU_CR = 0x20  # Cycle ready (read-only)
PU_OSCS = 0x40  # Oscillator select
PU_AVDDS = 0x80  # AVDD source select


def main():
    print("=" * 60)
    print("NAU7802 Scale Diagnostic")
    print("=" * 60)

    print(f"Configured bus: {I2C_BUS}, address: 0x{NAU7802_ADDR:02X}")

    # Probe both common I2C buses and show where devices are actually visible.
    found_by_bus: dict[int, list[int]] = {}
    for bus_num in (0, 1):
        found_by_bus[bus_num] = []
        try:
            with smbus2.SMBus(bus_num) as probe_bus:
                for addr in range(0x03, 0x78):
                    try:
                        probe_bus.read_byte(addr)
                        found_by_bus[bus_num].append(addr)
                    except OSError:
                        continue
        except FileNotFoundError:
            continue
        except PermissionError:
            continue

    for bus_num, addrs in found_by_bus.items():
        if addrs:
            pretty = " ".join(f"0x{a:02X}" for a in addrs)
            print(f"Bus {bus_num} devices: {pretty}")
        else:
            print(f"Bus {bus_num} devices: (none)")

    if NAU7802_ADDR not in found_by_bus.get(I2C_BUS, []):
        for alt in (1, 0):
            if alt != I2C_BUS and NAU7802_ADDR in found_by_bus.get(alt, []):
                print(f"\nHint: NAU7802 (0x{NAU7802_ADDR:02X}) appears on bus {alt}, not configured bus {I2C_BUS}.")
                print(f"Try: SPOOLBUDDY_I2C_BUS={alt} .../scale_diag.py")
                break

    scale = NAU7802()
    try:
        print("[1] Initializing...")

        scale.init()

        # Print key interpreted config values
        revision = scale.read_reg(REG_REVISION)
        revision_id = revision & 0x0F
        print(f"    Revision ID: {revision_id}")

        # PU_CTRL bit 7: AVDD source select
        pu_ctrl = scale.read_reg(REG_PU_CTRL)
        avdds = (pu_ctrl >> 7) & 0x1
        avdds_str = "Internal LDO" if avdds == 1 else "AVDD pin input"
        print(f"    AVDD source: {avdds_str}")
        ctrl1 = scale.read_reg(REG_CTRL1)
        vldo = (ctrl1 >> 3) & 0b111
        vldo_map = {
            0b111: "2.4V",
            0b110: "2.7V",
            0b101: "3.0V",
            0b100: "3.3V",
            0b011: "3.6V",
            0b010: "3.9V",
            0b001: "4.2V",
            0b000: "4.5V",
        }
        vldo_str = vldo_map.get(vldo, f"Unknown ({vldo})")
        gain = ctrl1 & 0b111
        gain_map = {
            0b000: "1x",
            0b001: "2x",
            0b010: "4x",
            0b011: "8x",
            0b100: "16x",
            0b101: "32x",
            0b110: "64x",
            0b111: "128x",
        }
        gain_str = gain_map.get(gain, f"Unknown ({gain})")
        print(f"    LDO setting (VLDO): {vldo_str}")
        print(f"    Gain setting: {gain_str}")
        ctrl2 = scale.read_reg(REG_CTRL2)
        sps = (ctrl2 >> 4) & 0b111
        sps_map = {
            0b000: "10 SPS",
            0b001: "20 SPS",
            0b010: "40 SPS",
            0b011: "80 SPS",
            0b100: "320 SPS",
        }
        sps_str = sps_map.get(sps, f"Unknown ({sps})")
        print(f"    Sample rate: {sps_str}")
        adc = scale.read_reg(REG_ADC)
        chopper = (adc >> 4) & 0b11
        chopper_str = {0b00: "Enabled", 0b01: "Enabled", 0b10: "Enabled", 0b11: "Disabled"}.get(
            chopper, f"Unknown ({chopper})"
        )
        print(f"    ADC chopper: {chopper_str}")
        pga = scale.read_reg(REG_PGA)
        low_esr = (pga >> 6) & 0x1
        low_esr_str = "Enabled" if low_esr == 0 else "Disabled"
        print(f"    PGA low-ESR caps: {low_esr_str}")

        print("[2] Waiting for first reading...")
        for _ in range(200):
            if scale.data_ready():
                break
            time.sleep(0.010)
        else:
            print("    Timeout waiting for data ready")
            sys.exit(1)

        print("    Reading 10 samples (10 SPS = ~1 second)...")
        readings = []
        for i in range(10):
            # Wait for data ready
            for _ in range(200):
                if scale.data_ready():
                    break
                time.sleep(0.010)
            raw = scale.read_raw()
            readings.append(raw)
            print(f"    Sample {i + 1:2d}: {raw:>10d}")

        avg = sum(readings) / len(readings)
        spread = max(readings) - min(readings)
        print(f"\n    Average: {avg:>10.0f}")
        print(f"    Min:     {min(readings):>10d}")
        print(f"    Max:     {max(readings):>10d}")
        print(f"    Spread:  {spread:>10d}")

        print("\n" + "=" * 60)
        print("Diagnostic complete!")
        print("=" * 60)

    except Exception as e:
        print(f"\nERROR: {e}")
        is_known_error = False

        if isinstance(e, OSError):
            if e.errno == 16:  # Device or resource busy
                is_known_error = True
                print("\nI2C DEVICE BUSY (Errno 16): Another process is using the I2C bus.")
                print("This typically means the SpoolBuddy daemon is already reading the scale.")
                print("\nTo run this diagnostic, stop the daemon first:")
                print("  sudo systemctl stop bambuddy")
                print("  # Run diagnostic")
                print("  .../scale_diag.py")
                print("  # Restart daemon when done:")
                print("  sudo systemctl start bambuddy")
            elif e.errno == 121:
                is_known_error = True
                print("\nI2C NACK (Errno 121): the device did not acknowledge reads at 0x2A.")
                print("Check:")
                print("  - NAU7802 SDA/SCL are on the configured bus pins")
                print("  - 3.3V and GND are correct and stable")
                print("  - Sensor address is really 0x2A")
                print("  - No loose wire or swapped SDA/SCL")
            else:
                print(f"\nI2C Error (Errno {e.errno}): {e}")

        # Only print full traceback for unexpected errors
        if not is_known_error:
            import traceback

            traceback.print_exc()

        sys.exit(1)
    finally:
        scale.close()


if __name__ == "__main__":
    main()
