#!/usr/bin/env python3
"""Reproduce the contribution tables in
/posts/foreign-social-insurance-in-china/.

The 16% employer / 8% employee pension split, the two-thirds ratio and the
2030-2039 minimum-contribution series are all computed here and asserted, so a
reader can change the numbers and see where a claim stops holding.

Sources for the constants:
  - 8% personal account, employer share no longer credited to it:
    State Council decision 国发〔2005〕38号 (effective 2006-01-01),
    https://www.gov.cn/zwgk/2005-12/14/content_127311.htm
  - 16% employer pension rate: 国办发〔2019〕13号 (from 2019-05-01),
    https://www.gov.cn/gongbao/content/2019/content_5383716.htm
  - Minimum contribution period 15 -> 20 years, +6 months a year from 2030:
    NPC Standing Committee decision of 2024-09-13, art. 2 of the measures,
    https://www.gov.cn/yaowen/liebiao/202409/content_6974294.htm

Usage:
  python3 social-insurance-split.py                # default base 15000 CNY
  python3 social-insurance-split.py 22000          # your own monthly base

Code: MIT. Text and data on this site: see /data/LICENSE.txt.
"""

import sys

EMPLOYEE_RATE = 0.08   # 国发〔2005〕38号, personal account
EMPLOYER_RATE = 0.16   # 国办发〔2019〕13号, pooled fund
MIN_YEARS_2029 = 15.0  # Social Insurance Law art. 16
MIN_YEARS_2039 = 20.0  # NPC decision 2024, art. 2
RAMP_START_YEAR = 2030
RAMP_END_YEAR = 2039


def min_years(year):
    """Minimum contribution years needed at pension age, by the year you get there."""
    if year < RAMP_START_YEAR:
        return MIN_YEARS_2029
    if year >= RAMP_END_YEAR:
        return MIN_YEARS_2039
    # +6 months (0.5 year) for each year from 2030
    return MIN_YEARS_2029 + 0.5 * (year - RAMP_START_YEAR + 1)


def split(base, years):
    months = years * 12
    employee = base * EMPLOYEE_RATE * months
    employer = base * EMPLOYER_RATE * months
    return employee, employer


def main():
    base = float(sys.argv[1]) if len(sys.argv) > 1 else 15000.0

    # The claims the article makes, checked against the arithmetic.
    assert EMPLOYER_RATE == 2 * EMPLOYEE_RATE, "16/8 should be exactly double"
    e, r = split(base, 3)
    assert abs(r / (e + r) - 2 / 3) < 1e-12, "employer share should be two thirds"
    assert (1000 * EMPLOYEE_RATE, 1000 * EMPLOYER_RATE) == (80.0, 160.0), "per 1000 CNY of base: 80 and 160 a month"

    # Minimum-contribution series quoted in the table.
    series = {y: min_years(y) for y in range(2029, 2041)}
    assert series[2029] == 15
    assert series[2030] == 15.5
    assert series[2031] == 16
    assert series[2033] == 17
    assert series[2035] == 18
    assert series[2037] == 19
    assert series[2039] == 20
    assert all(series[y] <= MIN_YEARS_2039 for y in series)

    print(f"monthly contribution base: {base:,.0f} CNY")
    print(f"  employee pension: {base * EMPLOYEE_RATE:,.0f} / month")
    print(f"  employer pension: {base * EMPLOYER_RATE:,.0f} / month")
    print()
    print(f"{'years':>5}  {'employee 8%':>13}  {'employer 16%':>13}  {'returns':>13}")
    for y in (1, 3, 5, 10, 15):
        e, r = split(base, y)
        print(f"{y:>5}  {e:>13,.0f}  {r:>13,.0f}  {e:>13,.0f}")
    print()
    print("minimum contribution years, by the year you reach pension age")
    for y in sorted(series):
        print(f"  {y}: {series[y]:g}")
    print()
    print("VERDICT: all assertions passed")


if __name__ == "__main__":
    main()
